/* ==========================================================================
   Piren — fluid scale, page shell, shared patterns, animation library.

   CSS COMMENTS DO NOT NEST. An inner closer inside this block ends the whole
   block, and every line after it is parsed as CSS — which silently killed the
   :root ramp below and most of this file. Keep the code samples in these
   comments free of comment markers.

   COMPONENT STYLES ARE LINKED BY EVERY PAGE, not imported here and not
   co-located.

   A components/<name>/<name>.css file is bundled by the DEPLOY into
   assets/wl-components.<hash>.css, which does not exist in the source repo, so
   the Weblooks editor canvas loaded none of it. Moving the rules into
   assets/css/components.css and sections.css was not enough on its own either:
   pulled in with @import they 404'd on staging, because the deploy neither
   rewrites @import targets nor publishes the unhashed files they name. Every
   page now carries real <link> tags for all three, in this order:

       tokens → base → components → sections → layout

   ORDER IS LOAD-BEARING. layout.css re-declares every component root so the
   three breakpoint tiers at its foot have something to override, and those
   tiers only win if this file is read last.

   AND WHY ROOT RULES LIVE HERE AT ALL

   The deploy prefixes every selector in a co-located component stylesheet
   with [data-wl-c="<name>"], which can never match the component's own root —
   an element is not its own descendant. That is why root rules are written
   here as [data-wl-component="<name>"]. The co-located stylesheets have since
   been deleted; components.css and sections.css are the only copy.

   RULES THIS FILE ENFORCES
   • Type and space are fluid: every step is a clamp() driven by `cqi`, so
     nothing snaps at a breakpoint.
   • Breakpoints are CONTAINER queries, never viewport media queries.
   • No hardcoded pixels. px appears only for hairlines (--border-width-main)
     and for the 1px clip rect of .visually-hidden.
   • Text measures are in `ch`, from the scale below — never an ad-hoc number.

   THE THREE TIERS
   Taken from piren-bar.se, whose Webflow build breaks at 991 / 767 / 479px,
   and converted to rem so they scale with the reader's font size rather than
   with device pixels:

       62rem  (991px)  TABLET      nav collapses, 3-up grids go 2-up,
                                   side-by-side media blocks narrow
       48rem  (767px)  MOBILE      gutters tighten, grids go 1-up,
                                   row layouts stack to column
       30rem  (479px)  SMALL PHONE headings ease off

   NOT EVERY RESPONSIVE STEP BELONGS HERE. A page tier is the right tool when
   the PAGE is what changed — gutters, column counts, type curves. When what
   matters is how much room one block actually got, that block owns its own
   @container query instead, because card width is not monotonic with page
   width: going 2-up to 1-up at 48rem makes each card suddenly WIDER as the
   page narrows. event-list and the nav panel both decide that way.

   WHICH CONTAINER THE TIERS QUERY — and why it is `page`

   A @container rule can only restyle a DESCENDANT of the container it names;
   it can never restyle the element that establishes that container. So a rule
   that names `header` and then selects the header itself matches nothing,
   ever — the header IS `header`. Every responsive step on this site was
   written that way and none of them had fired: the mobile nav never appeared
   at any width, and the 2-up and 1-up grid steps never applied.

   The tiers below therefore query `page` (declared on <body> in base.css),
   of which every component root IS a descendant. That is also what these
   breakpoints honestly are: page-level layout steps, not per-component ones.

   `container-type` stays on each root, because it is what makes `cqi` inside
   a component resolve against that component's own width.
   ========================================================================== */

:root{

  /* --- Brand colour not in tokens.css -------------------------------------
     The Julbord brick. Belongs in tokens.css with the rest of the palette;
     it lives here because tokens.css is generated from the design and this
     hue arrived with the seasonal page instead. Cream on it is ~11:1. */
  --piren-brick: #862620;

  /* The consent notice sits above everything, including the header (100) and
     the nav panel (200) — it has to be answerable whatever else is open. */
  --layer-consent: 300;

  /* --- Text measures -------------------------------------------------------
     THE ONLY WIDTHS TEXT IS ALLOWED TO TAKE. All four are in `ch`, so a
     column holds the same number of characters whatever size its type is —
     which is what makes blocks in different sections look related even when
     their pixel widths differ.

     base.css puts --measure-prose on every <p>, so the default is already
     right; these exist for the cases that genuinely differ. Reach for a step
     on this scale, never for a bare number — the 52ch / 42ch / 34ch one-offs
     that used to be scattered through the components are exactly what made
     the sections look uneven.

       tight  a narrow column: nav cards, three-up grids
       short  secondary copy beside something else
       prose  running body copy                     (tokens.css, 50ch)
       wide   a copy column that IS the measure — the text fills it and the
              heading, body and button all share one right edge          */
  --measure-tight: 34ch;
  --measure-wide : 64ch;

  /* --- Fluid type ramp ----------------------------------------------------
     min ....... value at the narrowest supported container
     preferred . rem base + cqi growth
     max ....... the static ramp ceiling from tokens.css                    */
  --type-display : clamp(2.6rem,  1.72rem + 4.4cqi,  var(--display-font-size));
  --type-h1      : clamp(1.8rem,  1.18rem + 3.1cqi,  3.97rem);
  --type-h2      : clamp(1.6rem,  1.16rem + 2.21cqi, 3.15rem);
  --type-h3      : clamp(1.42rem, 1.11rem + 1.54cqi, var(--h3-font-size));
  --type-h4      : clamp(1.27rem, 1.07rem + 1.01cqi, 1.98rem);

  --type-xl      : clamp(1.14rem, 1.02rem + 0.55cqi, 1.5rem);
  --type-lead    : clamp(1.15rem, 1.05rem + 0.5cqi,  1.4rem);
  --type-body-lg : clamp(1.06rem, 0.98rem + 0.4cqi,  var(--text-large-font-size));
  --type-md      : clamp(var(--text-main-font-size), 0.94rem + 0.28cqi, 1.2rem);
  --type-body    : clamp(0.94rem, 0.91rem + 0.16cqi, 1.05rem);
  --type-sm      : clamp(0.84rem, 0.81rem + 0.15cqi, var(--text-small-font-size));
  --type-xs      : clamp(0.78rem, 0.76rem + 0.11cqi, 0.86rem);

  /* --- Fluid space ramp --------------------------------------------------- */
  --space-hair   : clamp(var(--size-0-5), 0.43rem + 0.3cqi,  var(--size-0-75));
  --space-tight  : clamp(var(--size-1),   0.83rem + 0.71cqi, var(--size-1-5));
  --space-snug   : clamp(1.75rem,         1.39rem + 1.5cqi,  var(--size-3));
  --space-loose  : clamp(var(--size-2),   1.43rem + 2.4cqi,  var(--size-4));
  --space-column : clamp(3.5rem,          2.21rem + 5.4cqi,  var(--size-8));

  /* Section rhythm */
  --section-y    : clamp(var(--size-3), 1.86rem + 4.8cqi, var(--padding-vertical-main));
  --section-y-sm : clamp(2.25rem,       1.46rem + 3.3cqi, var(--padding-vertical-small));

  /* Decorative brand glyphs */
  --decor-min    : clamp(var(--size-6), 3.14rem + 12cqi, var(--size-16));
  --decor-width  : 30cqi;

  /* Breathing room above an anchored section. The header does not stick, so
     this only has to keep a heading off the very top edge. */
  --header-offset: var(--space-snug);
}

/* ==========================================================================
   COMPONENT ROOTS
   These duplicate the root rules in components.css / sections.css with
   identical values, and win on ties because they come later. They are here
   because they are what the three tiers below reach for — and because a root
   rule cannot work from a co-located component stylesheet at all.
   ========================================================================== */

[data-wl-component="site-header"]{
  position:relative;
  z-index:var(--layer-header);
  display:flex;
  flex-direction:column;
  align-items:stretch;
  background:var(--surface-page);
  color:var(--text-body);
  font-family:var(--font-body);
  font-size:var(--type-body);
  font-weight:var(--font-body-regular);
  container-type:inline-size;
  container-name:header;
  --desk:flex;
  --burger:none;
  --drawer:none;
  --announce-note:inline;
  --bar-gutter:var(--space-tight);
  /* The floor is 3.5rem rather than 3rem so "Piren" stays readable on a
     phone. There used to be a flat 8rem override at the 30rem tier, which
     made the wordmark BIGGER on the smallest screens than anywhere else —
     that was the bug behind "logo too large on mobile". */
  --logo-max:clamp(3.5rem, 2.43rem + 2.86cqi, 5rem);
  /* Columns in the full-width nav panel. Three cards, one per hire type. */
  --mega-cols:repeat(3, 1fr);
  /* The card photograph caps at half the column it sits in, so the panel
     reads as a menu with pictures rather than a wall of photographs. */
  --nav-card-w:14rem;
  /* How much of the burger's 44px tap target the mark actually fills. */
  --burger-icon:62%;
}
[data-wl-component="site-header"][data-nav-active="menyer"] .nav-item[data-nav-id="menyer"] .nav-item__link,
[data-wl-component="site-header"][data-nav-active="julbord"] .nav-item[data-nav-id="julbord"] .nav-item__link,
[data-wl-component="site-header"][data-nav-active="abonnera"] .nav-item[data-nav-id="abonnera"] .nav-item__link,
[data-wl-component="site-header"][data-nav-active="evenemang"] .nav-item[data-nav-id="evenemang"] .nav-item__link,
[data-wl-component="site-header"][data-nav-active="hitta"] .nav-item[data-nav-id="hitta"] .nav-item__link{
  text-decoration:underline;
  text-underline-offset:.3em;
}

[data-wl-component="site-footer"]{
  position:relative;
  background:var(--piren-slate) var(--texture-foam) 50% / cover no-repeat;
  color:var(--text-body);
  font-family:var(--font-body);
  font-size:var(--type-body);
  font-weight:var(--font-body-regular);
  line-height:var(--line-height-normal);
  container-type:inline-size;
  container-name:footer;
}

/* The consent notice is fixed to the bottom edge and spans the screen. It is
   rendered from inside site-footer — the one component every page has exactly
   once — so `position:fixed` is what lifts it out of that footer and back to
   the viewport.

   AND THE THEME HAS TO COME BACK WITH IT. site-footer runs on
   .piren-theme-inverse, so a notice mounted inside it inherited cream-on-slate
   aliases and came out dark heading on dark ground, with body copy at
   --text-muted, which the inverse block does not even restate. Restating the
   :root aliases here pins the notice to the page's own theme wherever it is
   mounted; keep this list in step with the theme block in tokens.css. */
[data-wl-component="cookie-consent"]{
  position:fixed;
  inset-block-end:0;
  inset-inline:0;
  z-index:var(--layer-consent);
  font-family:var(--font-body);
  font-size:var(--type-body);
  font-weight:var(--font-body-regular);
  line-height:var(--line-height-normal);

  --surface-page:var(--piren-cream);
  --surface-card:var(--piren-cream);
  --text-body:var(--color-secondary-900);
  --text-heading:var(--color-secondary-900);
  --text-muted:var(--color-secondary-600);
  --border-color:var(--color-secondary-800);
  --border-color-hairline:var(--color-primary-200);
  --button-bg:var(--color-secondary-800);
  --button-text:var(--color-neutral-50);
  --button-bg-hover:var(--color-secondary-950);
  --button-text-hover:var(--color-neutral-50);
  --button-outline:var(--color-secondary-800);
  --link-color:var(--color-secondary-900);
  --link-color-hover:var(--color-secondary-950);
  color:var(--text-body);
}

/* hero-banner — a desktop-only band. --hero-h sizes it where it shows; on a
   phone it is not shrunk but removed outright, at the 48rem tier at the foot
   of this file. (piren-bar.se keeps a 5.5rem strip down to 479px; we do not —
   a letterbox of a cropped photograph reads as a loading artefact.) */
[data-wl-component="hero-banner"]{
  position:relative;
  overflow:clip;
  container-type:inline-size;
  container-name:hero;
  --hero-h:clamp(14rem, 30cqi, 24rem);
}

[data-wl-component="intro-split"]{
  container-type:inline-size;
  container-name:intro;
}

[data-wl-component="lunch-week"]{
  container-type:inline-size;
  container-name:lunch;
  --day-floor:30%;
}

[data-wl-component="menu-cards"]{
  container-type:inline-size;
  container-name:menus;
  --card-floor:var(--element-xmin);
}

[data-wl-component="media-split"]{
  container-type:inline-size;
  container-name:split;
}
[data-wl-component="media-split"].media-split--tint{
  background:var(--surface-tint) var(--texture-salt-lines) 50% / cover no-repeat;
}
/* The Julbord band. It flips the theme tokens wholesale rather than only the
   background, so the heading, the body copy and the button re-skin together
   instead of each needing an override of its own. */
[data-wl-component="media-split"].media-split--julbord,
[data-wl-component="julbord-hero"]{
  background:var(--piren-brick) var(--texture-foam) 50% / cover no-repeat;
  color:var(--piren-cream);
  --text-body:var(--piren-cream);
  --text-heading:var(--piren-cream);
  --text-muted:var(--color-primary-200);
  --border-color:var(--piren-cream);
  --border-color-hairline:var(--color-primary-200);
  --button-bg:var(--piren-cream);
  --button-text:var(--color-secondary-900);
  --button-bg-hover:var(--color-primary-200);
  --button-text-hover:var(--color-secondary-900);
  --button-outline:var(--piren-cream);
  --link-color:var(--piren-cream);
  --link-color-hover:var(--color-primary-200);
  --focus-ring-color:var(--piren-cream);
  --focus-ring-offset:var(--piren-brick);
}
[data-wl-component="media-split"].media-split--media-start .media-split__media{order:-1}

[data-wl-component="julbord-hero"]{
  position:relative;
  overflow:clip;
  container-type:inline-size;
  container-name:jbhero;
}

[data-wl-component="closing-cta"]{
  position:relative;
  container-type:inline-size;
  container-name:closing;
}
[data-wl-component="closing-cta"].closing-cta--tint{
  background:var(--surface-tint) var(--texture-salt-lines) 50% / cover no-repeat;
}

[data-wl-component="find-us"]{
  container-type:inline-size;
  container-name:find;
  --map-height:28rem;
}

[data-wl-component="gallery-strip"]{
  position:relative;
  inline-size:100%;
  padding-block:var(--space-snug);
  overflow-x:auto;
  overflow-y:hidden;
  scroll-behavior:auto;
  overscroll-behavior-x:contain;
  scrollbar-width:none;
  container-type:inline-size;
  container-name:strip;
  --tile-w:var(--element-min);
}
[data-wl-component="gallery-strip"]::-webkit-scrollbar{display:none}

/* menu-tabs — three tabs stay three tabs at every width; the type shrinks
   instead of the row stacking, because a stacked tablist stops reading as a
   switch. */
[data-wl-component="menu-tabs"]{
  position:relative;
  container-type:inline-size;
  container-name:menutabs;
  --tab-cols:repeat(3, 1fr);
  --tab-size:var(--type-h2);
}
/* Panels only start hiding once menu-tabs.js has added .is-tabbed. Before
   that — and forever, with script off — all three menus render in full. */
[data-wl-component="menu-tabs"].is-tabbed .menu-panel{display:none}
[data-wl-component="menu-tabs"].is-tabbed .menu-panel.is-active{display:block}

[data-wl-component="menu-group"]{
  container-type:inline-size;
  container-name:menugroup;
  --row-dir:row;
  --row-wrap:nowrap;
}

[data-wl-component="benefit-columns"]{
  position:relative;
  container-type:inline-size;
  container-name:benefits;
  --benefit-floor:min(100%, 18rem);
}

[data-wl-component="marquee-strip"]{
  position:relative;
  overflow:clip;
  inline-size:100%;
  padding-block-end:var(--section-y);
  display:flex;
  flex-direction:column;
  gap:var(--grid-gutter);
  container-type:inline-size;
  container-name:marquee;
  --tile-w:20rem;
  --grid-gutter:var(--size-2);
}

[data-wl-component="quote-band"]{
  position:relative;
  background:var(--piren-slate) var(--texture-foam) 50% / cover no-repeat;
  container-type:inline-size;
  container-name:quote;
}

[data-wl-component="photo-grid"]{
  position:relative;
  container-type:inline-size;
  container-name:photos;
  --grid-cols:repeat(3, 1fr);
  --grid-gutter:var(--size-2);
}

/* event-list — the column COUNT is a page decision and lives in the tiers
   below; whether a card sets its picture beside its words or above them is
   the card's own decision and lives in sections.css, as a query against each
   card. There is deliberately no --row-dir here: a page tier cannot express
   that step, because going 2-up to 1-up makes each card wider. */
[data-wl-component="event-list"]{
  position:relative;
  container-type:inline-size;
  container-name:events;
  --evt-cols:repeat(2, 1fr);
  --evt-img:8.5rem;
  /* Past events read as an archive rather than a listing — and since the
     group headings are hidden, this dimming is the ONLY thing distinguishing
     them on sight. Hovering or tabbing in lifts the group back to full
     strength, so it never costs a reader the content. */
  --past-opacity:.5;
}

[data-wl-component="contact-block"]{
  position:relative;
  container-type:inline-size;
  container-name:contact;
  --icon-size:var(--size-3);
}

/* ==========================================================================
   THE ANNOUNCEMENT BAND
   One <a> per current or upcoming event, rendered from the events collection.
   site-header.js removes the expired ones, keeps ONE at random, and turns it
   into a loop; this is what makes that behave, and what happens without it.
   ========================================================================== */

/* The repeat container is a node in the DOM and nothing in the layout, so the
   bands stay direct children of the header's flex column. */
.site-header__announce-set{display:contents}

/* WITHOUT JAVASCRIPT, THE FIRST BAND IS THE BAND. The rows arrive sorted by
   start date, so that is the soonest event — correct, just never shuffled.
   Once the script has removed the others, whichever it kept IS the first
   child, so this rule needs no counterpart to undo it. */
.site-header__announce-set > .site-header__announce:not(:first-child){display:none}

/* The window the loop travels through. The band already paints the ground and
   owns the hover state; this only clips. */
.site-header__announce-viewport{display:block;overflow:clip}

/* Still by default: one group, centred by .site-header__announce-inner's own
   max-width and auto margins, exactly as the hardcoded band used to sit. The
   script adds .is-marquee only once it has built two matched halves. */
.site-header__announce-track{display:block}

.site-header__announce-track.is-marquee{
  display:flex;
  inline-size:max-content;
  align-items:center;
  animation:piren-marquee var(--marquee-duration, 30s) linear infinite;
}
/* Inside the loop the group is a tile in a row, not a centred column: the
   auto margins and the 90rem cap would both fight the track. */
.site-header__announce-track.is-marquee .site-header__announce-inner{
  inline-size:max-content;
  max-inline-size:none;
  margin-inline:0;
  flex-wrap:nowrap;
  padding-inline:2.2ch;
}
/* The note is dropped on a phone by the 48rem tier below, which is a
   display:none — inside the track that would leave a gap in the middle of a
   tile rather than shortening it, so it is the whole tile that narrows. */

/* PAUSE ON HOVER AND ON FOCUS. The band is a link, and a moving target is a
   hard one to hit — for a trembling hand it is close to impossible. Stopping
   it while the pointer is on it costs nothing and makes it clickable. */
.site-header__announce:hover .site-header__announce-track.is-marquee,
.site-header__announce:focus-visible .site-header__announce-track.is-marquee{
  animation-play-state:paused;
}

/* ==========================================================================
   PAGE SHELL
   ========================================================================== */

.page-shell{
  display:flex;
  flex-direction:column;
  align-items:stretch;
  overflow:clip;
  color:var(--text-body);
  font-family:var(--font-body);
  font-size:var(--type-body);
  font-weight:var(--font-body-regular);
  line-height:var(--line-height-normal);
  text-wrap:pretty;

  /* Cross-component sizes.

     --hero-w is the FLOOR at which the square media block stops shrinking and
     the row wraps instead; --hero-max is its ceiling. Without the ceiling the
     block is flex:1 and simply takes half the row, which at the 90rem
     container is ~42rem of photograph — enough that the picture, not the
     words, became the section. The cap is about a fifth under that. */
  --tile:var(--element-min);   /* portrait media block, intro-split  */
  --hero-w:26rem;              /* square media block, floor          */
  --hero-max:34rem;            /* square media block, ceiling        */
}

/* ==========================================================================
   THE THREE TIERS
   Every page-level responsive step lives in these three blocks, queried
   against `page` so each rule restyles a DESCENDANT of the container rather
   than the container itself. Grouping them also makes the whole responsive
   behaviour of the site readable in one place.
   ========================================================================== */

/* --- TABLET — 991px ------------------------------------------------------- */
@container page (max-width: 62rem){

  .page-shell{
    --tile:18rem;
    --hero-w:22rem;
    --hero-max:26rem;
  }

  /* Five nav items plus a booking button stop fitting beside the wordmark
     well before phone width. piren-bar.se collapses here too. Hiding the nav
     also retires the full-width panel: the drawer lists the same three hire
     types as plain links, which is the right control on a touch screen. */
  [data-wl-component="site-header"]{--desk:none;--burger:flex;--drawer:block}

  [data-wl-component="lunch-week"]{--day-floor:45%}
  [data-wl-component="menu-cards"]{--card-floor:13rem}
  [data-wl-component="find-us"]{--map-height:24rem}
  [data-wl-component="gallery-strip"]{--tile-w:18rem}
  [data-wl-component="menu-tabs"]{--tab-size:var(--type-h3)}
  [data-wl-component="marquee-strip"]{--tile-w:16rem;--grid-gutter:var(--size-1-5)}
  [data-wl-component="photo-grid"]{--grid-cols:repeat(2, 1fr);--grid-gutter:var(--size-1-5)}
  [data-wl-component="event-list"]{--evt-img:7rem}
  [data-wl-component="contact-block"]{--icon-size:var(--size-2-5)}
}

/* --- MOBILE — 767px -------------------------------------------------------
   The fluid ramp keeps growing with cqi, so past this width the headings need
   a tighter curve or a long Swedish compound overflows. */
@container page (max-width: 48rem){

  .page-shell{
    --type-h1:clamp(1.6rem, 7cqi, 2.4rem);
    --type-h2:clamp(1.3rem, 5.6cqi, 1.9rem);
    --type-h3:clamp(1.2rem, 4.8cqi, 1.7rem);
    --type-h4:clamp(1.12rem, 4.2cqi, 1.4rem);
    /* Both media blocks go full bleed inside the gutters: a portrait frame
       floated at 20rem in a 24rem column reads as a mistake, not a choice. */
    --tile:100%;
    --hero-w:100%;
    --hero-max:100%;
  }

  [data-wl-component="site-header"]{--announce-note:none;--bar-gutter:var(--space-hair)}

  /* THE HOME BANNER IS GONE ON A PHONE. It used to collapse to a 5.5rem strip
     here (matching piren-bar.se) and only disappear at 30rem, but a letterbox
     of a cropped photograph shows nothing worth the scroll at any phone width
     — it reads as a loading artefact rather than a picture. The page now opens
     on the intro instead, which is the first thing with something to say. */
  [data-wl-component="hero-banner"]{display:none}
  /* And with no band above it there is nothing left to lift over — the
     overlap would drag the h1 up into the header. */
  .intro-split__row{margin-block-start:0}
  /* AND NO SECTION PADDING ABOVE THE REEL EITHER. With the banner gone the
     intro is the first thing under the header, and the full --section-y it
     carries as a mid-page section became a band of empty cream between the
     wordmark and the film, which the client circled as "a gap above the
     video". The header bar keeps its own padding, so a hairline of air is
     what is left. Desktop and tablet are untouched: there the banner shows
     and the row lifts up into it. */
  [data-wl-component="intro-split"] > .container{padding-block-start:var(--space-hair)}
  /* The reel takes the whole column. --tile is already 100% above, but the
     intro component's own 52rem query caps it at 78cqi for the tablet case,
     which on a phone left it inset inside its own column with a ragged strip
     of ground down one side. */
  .intro-split__media{max-inline-size:100%}

  [data-wl-component="lunch-week"]{--day-floor:100%}
  [data-wl-component="menu-cards"]{--card-floor:100%}
  [data-wl-component="find-us"]{--map-height:18rem}
  [data-wl-component="gallery-strip"]{--tile-w:min(72cqi, 20rem)}
  [data-wl-component="menu-tabs"]{--tab-size:var(--type-h4)}
  [data-wl-component="menu-group"]{--row-dir:column;--row-wrap:wrap}
  [data-wl-component="marquee-strip"]{--tile-w:min(78cqi, 20rem);--grid-gutter:var(--size-1)}
  [data-wl-component="photo-grid"]{--grid-cols:1fr;--grid-gutter:var(--size-1)}
  [data-wl-component="event-list"]{--evt-cols:1fr;--evt-img:min(30cqi, 9rem)}
  [data-wl-component="contact-block"]{--icon-size:2.25rem}
}

/* --- SMALL PHONE — 479px --------------------------------------------------
   "Abonneringsförfrågan" and "vattenutsikt" are the words that decide this
   step. */
@container page (max-width: 30rem){

  .page-shell{
    --type-h1:clamp(1.5rem, 8.4cqi, 2rem);
    --type-h2:clamp(1.25rem, 6.6cqi, 1.6rem);
    --type-h3:clamp(1.15rem, 5.6cqi, 1.45rem);
  }
}

/* ==========================================================================
   LAYOUT PRIMITIVES
   ========================================================================== */

/* The 90rem content cap with fluid gutters.

   position:relative so an absolutely positioned child — the section rule —
   measures against the CONTENT column rather than falling through to the
   full-bleed section and stretching across the whole viewport. */
.container{
  position:relative;
  width:100%;
  max-width:var(--max-width-main);
  margin-inline:auto;
  padding:var(--section-y) var(--space-tight);
}
.container--tight{padding-block:var(--section-y-sm)}
/* For a section that continues the one above it rather than starting a new
   band — the padding is already spent by its neighbour. */
.container--flush-top{padding-block-start:0}
.container--flush-bottom{padding-block-end:0}
/* The plain container, named. benefit-columns emits container--<spacing> and
   needs both values to be real classes; this one restates the default so the
   pairing is explicit rather than relying on a class that matches nothing. */
.container--band{padding-block-start:var(--section-y)}

/* A section is the positioning context for its decorative glyph. */
.section{position:relative}
.section--clip{overflow:clip}

/* Vertical rhythm helper: one gap token for a column of children. */
.stack{display:flex;flex-direction:column;align-items:stretch;gap:var(--stack-gap,var(--space-tight))}
.stack--center{align-items:center;text-align:center}

/* Horizontal group that wraps. */
.cluster{display:flex;flex-flow:row wrap;align-items:center;gap:var(--cluster-gap,var(--space-tight))}

/* ==========================================================================
   SHARED PATTERNS
   Each of these is used by more than one component. Defined once here so a
   single edit restyles every occurrence.
   ========================================================================== */

/* --- Rich text --------------------------------------------------------------
   base.css sets `p{margin:0}`, which is right for a site built out of flex
   columns that space their children with `gap` — but wrong the moment a block
   emits actual prose. A slot filled with two paragraphs, or a CMS rich-text
   field that returns several, renders them as plain siblings with nothing
   between them, so the second sentence butts straight onto the first.

   Opt those containers in with .rich-text rather than making `p + p` global:
   most paragraphs on this site sit in a flex container that already spaces
   them, and a global rule would double the gap in every one.

   `> * + *` rather than `p + p` so a list or a blockquote coming back from a
   rich-text field is spaced the same as a paragraph.                        */
.rich-text > * + *{margin-block-start:var(--space-tight)}

/* --- Button ---------------------------------------------------------------
   Rendered on <a> and <button> alike. Colours come from the button-* tokens,
   so .piren-theme-inverse and the brick theme re-skin it for free.         */
.btn{
  display:inline-flex;
  justify-content:center;
  align-items:center;
  gap:var(--space-hair);
  padding:.65em 1.2em;
  border:var(--border-width-main) solid transparent;
  background:var(--button-bg);
  color:var(--button-text);
  font-family:var(--font-body);
  font-weight:var(--font-body-regular);
  font-size:var(--type-body);
  line-height:var(--line-height-normal);
  text-align:center;
  text-decoration:none;
  white-space:nowrap;
  cursor:pointer;
  transition:var(--transition-hover);
}
.btn:hover{background:var(--button-bg-hover);color:var(--button-text-hover)}
.btn--block{display:flex;width:100%;white-space:normal}
.btn--outline{
  background:transparent;
  color:var(--button-outline);
  border-color:var(--button-outline);
}
.btn--outline:hover{background:var(--button-bg);color:var(--button-text)}

/* --- Emphasised inline link ------------------------------------------------ */
.link-strong{
  font-weight:var(--font-body-medium);
  text-decoration:underline;
  text-underline-offset:.18em;
}

/* --- Section rule ----------------------------------------------------------
   diamond — line — DIAMOND — line — diamond, drawn from one element plus one
   child: the bar is a gradient, the end marks are pseudo-elements, the centre
   mark is the child.

   It spans its POSITIONED ancestor, so it must sit inside the component's
   inner wrapper (or, failing that, the .container) — never loose in a
   full-bleed section, where it would run the width of the viewport and read
   as a different, longer rule than the same mark elsewhere on the site.

   --rule-offset is how far above that wrapper the rule is pulled. The default
   clears a full section's padding, so the rule lands on the seam between two
   sections; a smaller value tucks it just above the content instead.       */
.section-rule{
  position:absolute;
  inset-block-start:calc(var(--rule-offset, var(--section-y)) * -1);
  inset-inline:0;
  display:flex;
  justify-content:center;
  align-items:center;
  block-size:1rem;
  pointer-events:none;
  background:linear-gradient(
    to bottom,
    transparent calc(50% - var(--border-width-main) / 2),
    var(--text-heading) calc(50% - var(--border-width-main) / 2),
    var(--text-heading) calc(50% + var(--border-width-main) / 2),
    transparent calc(50% + var(--border-width-main) / 2)
  );
}
.section-rule::before,
.section-rule::after,
.section-rule__mark{
  content:"";
  display:block;
  flex:none;
  background:var(--text-heading);
  transform:rotate(45deg);
}
.section-rule::before,
.section-rule::after{
  position:absolute;
  inline-size:.5rem;
  block-size:.5rem;
}
.section-rule::before{inset-inline-start:0}
.section-rule::after {inset-inline-end:0}
.section-rule__mark{inline-size:1rem;block-size:1rem}

/* Sits at the section's own top edge instead of straddling the seam above. */
.section-rule--top{inset-block-start:0}

/* --- Media frame -----------------------------------------------------------
   A still photograph with a muted video layered over it. The photo is the
   poster: if the video is blocked, paused for reduced motion, or still
   loading, the frame never shows an empty box.                            */
.media-frame{
  position:relative;
  inline-size:100%;
  background:var(--color-secondary-600);
  background-image:var(--overlay-dark);
}
.media-frame::after{
  content:"";
  position:absolute;
  inset:0;
  background:var(--surface-dark);
  opacity:.1;
  pointer-events:none;
}
.media-frame__img,
.media-frame__video{
  position:absolute;
  inset:0;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
}
.media-frame__video{opacity:0;transition:opacity var(--duration-slow) var(--ease-default)}
.media-frame__video.is-playing{opacity:1}
.media-frame--portrait{aspect-ratio:2/3}
.media-frame--square{aspect-ratio:1}

/* --- Decorative brand glyph ------------------------------------------------
   The hand-drawn watermarks. Positioned and sized by modifier; always behind
   content and never interactive. Every glyph SVG ships at full strength — the
   opacity below is the only place it is dimmed, so a source file with the
   dimming baked in would compound to invisible.                           */
.decor-glyph{
  position:absolute;
  z-index:var(--layer-decor);
  inline-size:var(--decor-width);
  max-inline-size:50rem;
  min-inline-size:var(--decor-min);
  mix-blend-mode:multiply;
  opacity:.12;
  pointer-events:none;
}
.decor-glyph--sun{
  inset-block-end:0;
  inset-inline-start:22%;
  max-inline-size:44rem;
  opacity:.16;
}
.decor-glyph--oysters{inset:auto 0 -2% auto}
.decor-glyph--palms  {inset:auto auto -2% 0}

.decor-glyph--utensils  {inset:auto auto -2% 0;    --decor-width:22cqi; max-inline-size:32rem}
.decor-glyph--wine-glass{inset:auto 0 0 auto;      --decor-width:26cqi; max-inline-size:34rem}
.decor-glyph--cocktail  {inset:auto 0 8% auto;     --decor-width:20cqi; max-inline-size:28rem}
.decor-glyph--compass   {inset:auto auto -10% -4%; --decor-width:24cqi; max-inline-size:32rem}

/* On the brick ground the multiply blend turns the glyph into a muddy stain
   rather than a watermark. screen at a lower strength keeps it a highlight. */
.media-split--julbord .decor-glyph,
.julbord-hero .decor-glyph{mix-blend-mode:screen;opacity:.1}

/* ==========================================================================
   ANIMATION LIBRARY
   Four keyframes, two durations, one easing — every motion on the site is
   built from these. Anything that moves must honour prefers-reduced-motion.
   ========================================================================== */

@keyframes piren-fade-in { from{opacity:0}                              to{opacity:1} }
@keyframes piren-rise-in { from{opacity:0;transform:translateY(1.5rem)} to{opacity:1;transform:none} }
@keyframes piren-scale-in{ from{opacity:0;transform:scale(.96)}         to{opacity:1;transform:none} }

/* The announcement band's loop. -50% of a track built as two identical
   halves, so the frame it ends on is pixel-for-pixel the frame it starts on
   and the restart is invisible. site-header.js is what guarantees the halves
   match; this only moves them. */
@keyframes piren-marquee { from{transform:translateX(0)} to{transform:translateX(-50%)} }

/* Reveal-on-scroll. Elements opt in with data-reveal; assets/js/site.js adds
   .is-revealed when they enter the viewport. Without JS they stay visible.

   NOTE FOR ANYONE DIMMING A REVEALED ELEMENT: the animation ends on opacity 1
   and `both` keeps that end state, so an `opacity` declared on the SAME
   element loses to it however specific the selector is. Dim an ancestor that
   is not itself a [data-reveal] — which is what event-list does to fade its
   past group. */
[data-reveal]{opacity:1}
.js [data-reveal]{opacity:0}
.js [data-reveal].is-revealed{
  /* The declared end state, NOT the animation, is what guarantees the content
     is readable. If animation time never advances — stalled compositor, a
     webview that does not tick, an extension blocking animations — the
     element would otherwise keep the 0% frame and stay invisible forever.
     While the animation plays it wins; if it never plays, this shows. */
  opacity:1;
  animation:piren-rise-in var(--duration-slow) var(--ease-exit) both;
  animation-delay:var(--reveal-delay,0s);
}
.js [data-reveal="fade"].is-revealed { animation-name:piren-fade-in }
.js [data-reveal="scale"].is-revealed{ animation-name:piren-scale-in }

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
  .js [data-reveal]{opacity:1}
  .media-frame__video{opacity:1}
  /* The blanket rule above would leave the marquee frozen at translateX(0)
     with the track still laid out as two halves — the second half hanging off
     the side of a clipped box. site-header.js does not add .is-marquee under
     reduced motion, so this is only the belt to that brace. */
  .site-header__announce-track.is-marquee{
    animation:none !important;
    display:block;
    inline-size:auto;
  }
}

/* ==========================================================================
   UTILITY PAGES — 404 and the style guide. Short blocks of prose that need a
   readable measure and nothing else.
   ========================================================================== */

.utility{
  display:flex;
  flex-direction:column;
  align-items:flex-start;
  justify-content:center;
  gap:var(--space-tight);
  min-block-size:40vh;
}
.utility__lead{
  max-inline-size:var(--measure-prose);
  font-size:var(--type-lead);
  line-height:var(--line-height-normal);
}
.utility__actions{
  display:flex;
  flex-flow:row wrap;
  align-items:center;
  gap:var(--space-tight);
  margin-block-start:var(--space-hair);
}
