/* ==========================================
   LAGOA BRAND THEME
   Palette + typeface for Município de Lagoa, replacing Moncorvo's
   "Acto"/generic-sans branding. Loaded AFTER style.css/bento-grid.css so
   it can safely override without needing !important everywhere.
   Source: design mockups in /templates/assets/css/style.css (workspace
   root) + the old Ionic app's bundled Helvetica Neue
   (simsmart_lagoa_old/.../www/fonts/helvetica/).
========================================== */

:root {
  /* Category palette - the category -> color mapping used across the bento
     tiles below. */
  --lagoa-red: #D13239;      /* Município / Informações */
  --lagoa-red-dark: #8f1f24;
  --lagoa-orange: #F28D3C;   /* Gastronomia / Transportes */
  --lagoa-orange-dark: #c96c22;
  --lagoa-green: #63A830;    /* Alojamento / Eventos */
  --lagoa-green-dark: #3f7a1c;
  --lagoa-teal: #003B49;     /* Atividades / Notícias / Editais */
  --lagoa-teal-dark: #001f27;
  --lagoa-brown: #3E3934;    /* Produtos Locais / body text */
  --lagoa-brown-dark: #221f1c;
  --lagoa-grey: #C9C9C9;
  --lagoa-dark-grey: #4E4E4E;

  /* RGB triplets for use inside rgba(var(--x-rgb), alpha) - custom
     properties can't be interpolated inside rgba() as hex, only as
     comma-separated components. */
  --lagoa-red-rgb: 209, 50, 57;
  --lagoa-orange-rgb: 242, 141, 60;
  --lagoa-green-rgb: 99, 168, 48;
  --lagoa-teal-rgb: 0, 59, 73;
  --lagoa-brown-rgb: 62, 57, 52;
}

/* --- Typeface --- */
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-Thin.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-Thin.woff") format("woff");
  font-weight: 100;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-Light.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-Light.woff") format("woff");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-Italic.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-Italic.woff") format("woff");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-Medium.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-Medium.woff") format("woff");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-Bold.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-Bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Helvetica Neue";
  src: url("/static/fonts/helvetica/HelveticaNeue-BoldItalic.woff2") format("woff2"),
       url("/static/fonts/helvetica/HelveticaNeue-BoldItalic.woff") format("woff");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

html,
body {
  font-family: "Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* style.css declares `* { font-family: "Roboto" ... }`, which applies
   directly to every element and therefore beats plain inheritance from
   html/body above - the reset has to be re-stated at the same specificity
   from this later stylesheet to actually take effect. */
*,
*::before,
*::after {
  font-family: "Helvetica Neue", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* Moncorvo's brand faces (Acto, Editor) are hardcoded in ~25 class
   selectors throughout style.css, which the `*` reset above can't reach.
   Rather than edit every one, re-point those family names at the Helvetica
   Neue files the old Lagoa kiosk shipped: when several @font-face rules
   declare the same family/weight/style, the last one loaded wins, so these
   shadow the Acto/Editor definitions in style.css. Delete this block (and
   nothing else) to put the original faces back. */
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-Thin.woff2") format("woff2");
  font-weight: 100 200;
  font-style: normal;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-Medium.woff2") format("woff2");
  font-weight: 500 600;
  font-style: normal;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-Bold.woff2") format("woff2");
  font-weight: 700 900;
  font-style: normal;
}
@font-face {
  font-family: "Acto";
  src: url("/static/fonts/helvetica/HelveticaNeue-BoldItalic.woff2") format("woff2");
  font-weight: 700 900;
  font-style: italic;
}
@font-face {
  font-family: "Acto Hairline";
  src: url("/static/fonts/helvetica/HelveticaNeue-Thin.woff2") format("woff2");
  font-weight: 100 400;
  font-style: normal;
}
@font-face {
  font-family: "Acto UltraBlack";
  src: url("/static/fonts/helvetica/HelveticaNeue-Bold.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
}
@font-face {
  font-family: "Editor";
  src: url("/static/fonts/helvetica/HelveticaNeue-Light.woff2") format("woff2");
  font-weight: 100 400;
  font-style: normal;
}
@font-face {
  font-family: "Editor";
  src: url("/static/fonts/helvetica/HelveticaNeue-Medium.woff2") format("woff2");
  font-weight: 500 900;
  font-style: normal;
}

/* --- Header ---
   The old kiosk put a large centred logo at the top of the home screen with
   only a thin, light-weight clock in the corner (templates/home.html:
   .logo-central + .data).

   Two layouts, and the header moves between them whenever a tile is opened or
   closed: this one for the home screen, and body.content-header further down for
   a section. They're kept mutually exclusive rather than left to specificity -
   :not(.content-header) here - because the transition between them is a
   measured flight (headerFlight() in bentoAnimation.js) and a measurement taken
   while both sets of rules matched would aim at the wrong place. */
/* Band height comes straight off the original 1080x1920 canvas: the logo
   started 140px down, and the mosaic didn't begin until y=618 (logo 208px
   tall + 70px + the search field + 90px), i.e. the top 32% of the screen was
   open white space. The grid height below subtracts exactly this figure, so
   the two must be edited together. Explicit box-sizing/height (rather than
   min-height) so that stays true regardless of the header's own padding. */
body:not(.content-header):has(.bento-container:not(.sidebar-mode)) .top-header {
  justify-content: center;
  position: relative;
  box-sizing: border-box;
  height: 32vh;
  min-height: 32vh;
}

body:not(.content-header):has(.bento-container:not(.sidebar-mode)) .top-header .logo-container {
  justify-content: center;
}

/* The original centred a 420px-wide logo on a 1080px canvas - 39vw - and
   sized by width, not height (the mark is 681x337). */
body:not(.content-header):has(.bento-container:not(.sidebar-mode)) .top-header .logo-header {
  width: min(420px, 39vw);
  max-width: none;
  height: auto;
}

/* Clock against the left margin, weather against the right, in both layouts:
   the block spans the header so its two children are pushed to opposite ends,
   rather than travelling together as one corner cluster. style.css groups them
   with a 60px gap for that cluster, which space-between makes moot.

   Pinned the same distance from the top in both layouts, so neither child moves
   when the header changes height between them - which is why the flight further
   down only has the logo to carry.

   Scoped to the two layouts rather than left unconditional: a section URL opened
   as a full page load has no mosaic to match on, so neither applies and the
   header falls back to style.css, where the logo is in the flow on the left and
   a clock spanning the band would sit on top of it. */
body:has(.bento-container) .top-header .header-right,
body.content-header .top-header .header-right {
  position: absolute;
  top: 26px;
  left: 40px;
  right: 40px;
  justify-content: space-between;
  align-items: flex-start;
  padding: 0;
}

/* Against the left margin the time and the date want to line up down their
   left edge; style.css right-aligns them, for a block that used to sit in the
   top-right corner. */
body:has(.bento-container) .top-header .hora-data,
body.content-header .top-header .hora-data {
  align-items: flex-start;
}

/* The header over an open section: the same arrangement as the home screen -
   clock and weather out of the flow at either margin, logo centred on the full
   width - on a 240px band, which is where the rail and the content panel start.
   Only the band's height and the logo's size differ, so the transition between
   the two is the logo shrinking into a shorter header and nothing else. */
body.content-header .top-header {
  justify-content: center;
  position: relative;
  box-sizing: border-box;
  height: 240px;
  min-height: 240px;
}

body.content-header .top-header .logo-container {
  justify-content: center;
}

/* Sized to clear what sits either side of it: the clock reaches roughly 400px
   in from the left and the weather about as far in from the right, so a centred
   logo much wider than this starts to meet them on a 1080-wide screen. */
body.content-header .top-header .logo-header {
  width: min(240px, 22vw);
  max-width: none;
  height: auto;
}


/* Clock/temperature were 700-weight; the old kiosk set its clock in 200 and
   relied on size alone for hierarchy. */
.top-header .hora-data .hora,
.top-header .meteo-uv .meteo .temperatura {
  font-size: 3em;
  font-weight: 200;
}

.top-header .hora-data .data {
  font-weight: 300;
  text-transform: uppercase;
}

/* Mid-weight stroke weather SVGs — lighter than the old filled set,
   still readable next to weight-200 type. */
.top-header .meteo-uv .meteo .meteo-ico {
  width: 48px;
  height: 48px;
  margin-right: 12px;
}

/* --- Bento tiles: solid brand colour + icon, no photography ---
   Per the menu redesign decision (see menu-structure-draft.md / plan
   "Lagoa Menu Restructure and Redesign"): tiles are solid brand-colour
   fills with a white icon + label, not photo backgrounds. .bento-overlay
   used to be a semi-transparent gradient tinting a photo underneath; now
   it *is* the tile's visible surface, so it needs to be fully opaque.
   The .bento-bg-image photo layer is removed from the templates, but is
   also hidden here defensively in case any template still references it. */
.bento-bg-image {
  display: none;
}

.bento-municipio .bento-overlay {
  background: linear-gradient(135deg, var(--lagoa-red) 0%, var(--lagoa-red-dark) 100%);
}

.bento-comer .bento-overlay,
.bento-bus .bento-overlay {
  background: linear-gradient(135deg, var(--lagoa-orange) 0%, var(--lagoa-orange-dark) 100%);
}

.bento-ficar .bento-overlay,
.bento-agenda .bento-overlay {
  background: linear-gradient(135deg, var(--lagoa-green) 0%, var(--lagoa-green-dark) 100%);
}

.bento-noticias .bento-overlay,
.bento-visitar .bento-overlay {
  background: linear-gradient(135deg, var(--lagoa-teal) 0%, var(--lagoa-teal-dark) 100%);
}

.bento-fazer .bento-overlay {
  background: linear-gradient(135deg, var(--lagoa-brown) 0%, var(--lagoa-brown-dark) 100%);
}

/* ==========================================
   HOME JIGSAW MENU
   Rebuild of the home tiles as the old kiosk's interlocking mosaic - see
   templates/home.html + the `.menu` block in templates/assets/css/style.css:
   three full-bleed columns with no gaps, each tile's top edge cut on a
   diagonal so neighbouring tiles slot into one another.

   The original hardcoded absolute pixel positions onto a fixed 1080x1920
   canvas. The same silhouette is rebuilt here on a 3-column grid so it
   scales to any kiosk resolution: rows 2 and 3 are pulled up by
   --jigsaw-overlap so each row's diagonal top edge covers the row above,
   and each tile's own --cut (which must stay <= the overlap, or a white
   wedge shows through the seam) varies the angle so the cuts read as
   hand-cut rather than mechanical, as in the original.

   Everything is scoped to :not(.sidebar-mode) because the same tiles get
   reused as the 100x100 sidebar buttons once a category is open, where an
   angled cut and a fixed grid position would both break - see
   animateToSidebar() in base/static/js/bentoAnimation.js.
========================================== */

/* Declared on the container unconditionally, not inside the :not(.sidebar-mode)
   rule below. The tiles inherit these and use them in calc(), and a custom
   property that blinks out of existence when the sidebar class goes on leaves
   those calcs unresolved for a recalc afterwards - which is exactly when the
   return animation measures the mosaic it has to fly back to. */
.bento-container {
  --jigsaw-overlap: 44px;
  /* Deepest cut taken out of the bottom row's bottom edge; the grid is given
     this much extra height so the slant trims the tiles rather than shortening
     the mosaic. */
  --jigsaw-foot: 34px;
}

.bento-container:not(.sidebar-mode) {

  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* Tracks are sized so the *visible* rows land on the original mosaic's
     352 / 317 / 370 proportions. They aren't the same thing: rows 2-3 are
     pulled up by --jigsaw-overlap below, which hands row 3 that many extra
     visible pixels and takes them off row 1, so row 1's track is given the
     overlap back. minmax(0, ...) rather than a bare fr because an fr track
     can't shrink below its content, so on a short screen the three rows
     would push each other down through the footer instead of sharing out
     the space available. */
  grid-template-rows: minmax(0, 1.258fr) minmax(0, 1fr) minmax(0, 1.02fr);
  gap: 0;
  box-sizing: border-box;
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  /* bento-grid.css re-declares .top-header as position:relative (overriding
     the absolute positioning in style.css), so the header sits in normal flow
     and already pushes this grid down by its own height - only the footer,
     which *is* absolute, needs reserving here.
     32vh header + 57vh mosaic + 11vh footer band. The original's proportions
     (a 980px mosaic over a 168px footer on 1920) left too much dead white
     between the last row and the footer controls on a real screen, so the
     mosaic takes 6vh of it back. Keep in step with the header height above. */
  /* Reach the footer and overlap past --jigsaw-foot so the zig-zag cut
     locks onto the footer fill with no white strip between the two edges.
     32vh header + 168px footer; +28px pulls the mosaic into the band. */
  height: calc(100vh - 32vh - 168px + var(--jigsaw-foot) + 28px);
  padding: 0;
  transition: margin-top 0.35s ease, height 0.35s ease;
}

/* Accessibility on the home mosaic: drop the grid into the wheelchair reach
   zone AND compress the three rows so the bottom edge still clears the
   footer. Content pages already do this for the left rail / content panel
   (style.css + handleAccessibilityToggle); the home grid only used to get
   a top offset, which hid the last row behind the footer controls. */
body.accessibility-mode .bento-container:not(.sidebar-mode) {
  margin-top: 15vh;
  /* Compressed home mosaic: keep the same footer lock as the normal layout. */
  height: calc(100vh - 32vh - 15vh - 168px + var(--jigsaw-foot) + 28px);
}

body.accessibility-mode .bento-container:not(.sidebar-mode) .bento-icon img {
  height: clamp(36px, 5vw, 64px);
}

/* Row 1 spans two columns for Município, mirroring the prominence the
   Moncorvo grid also gave it. Placement is explicit rather than automatic
   so the colour sequence stays checkerboarded (no two tiles of the same
   brand colour touching), while DOM order - which is what the sidebar
   buttons are stacked in - stays a sensible menu order. */
.bento-container:not(.sidebar-mode) > .bento-municipio { grid-area: 1 / 1 / 2 / 3; }
.bento-container:not(.sidebar-mode) > .bento-noticias  { grid-area: 1 / 3 / 2 / 4; }
.bento-container:not(.sidebar-mode) > .bento-ficar     { grid-area: 2 / 1 / 3 / 2; }
.bento-container:not(.sidebar-mode) > .bento-comer     { grid-area: 2 / 2 / 3 / 3; }
.bento-container:not(.sidebar-mode) > .bento-agenda    { grid-area: 2 / 3 / 3 / 4; }
.bento-container:not(.sidebar-mode) > .bento-visitar   { grid-area: 3 / 1 / 4 / 2; }
.bento-container:not(.sidebar-mode) > .bento-fazer     { grid-area: 3 / 2 / 4 / 3; }
.bento-container:not(.sidebar-mode) > .bento-bus       { grid-area: 3 / 3 / 4 / 4; }

/* Per-tile wedge depths. Unconditional, for the reason given on the container
   variables above; only what's *done* with them is scoped to the mosaic. */
.bento-container > .bento-item {
  --cut-l: 0px;
  --cut-r: 0px;
  /* Only the bottom row sets these - everywhere else the tile below covers
     the bottom edge, so cutting it would just open a gap. */
  --cut-bl: 0px;
  --cut-br: 0px;
  /* Deepest --cut-* in use; the row overlap has to be at least this much or
     the seam shows a wedge of page background. */
  --cut: 40px;
}

/* Each row's seam is a single continuous polyline: neighbouring tiles share
   the vertex height where they meet (Município's right corner = Notícias'
   left corner, and so on), which is what makes the cuts line up at the
   column boundaries instead of stepping. The original did the same thing by
   hand - e.g. gastronomia ended at 12px and alojamento started at 12px.
   Edit these in pairs. */
/* The top row's edge is the one the eye reads against the header's whitespace.
   Município starts nearly flush and the line climbs to its deepest point at the
   column boundary, then falls away to flush again at the right - a shallow peak
   rather than one continuous rise. Values set by eye on the real screen. */
.bento-container > .bento-municipio { --cut-l: 9px; --cut-r: 31px; }
.bento-container > .bento-noticias  { --cut-l: 31px; --cut-r: 0px; }

.bento-container > .bento-ficar     { --cut-l: 20px; --cut-r: 40px; }
.bento-container > .bento-comer     { --cut-l: 40px; --cut-r: 12px; }
.bento-container > .bento-agenda    { --cut-l: 12px; --cut-r: 32px; }

/* The bottom row is cut on both edges, so the mosaic finishes on the same
   zigzag it's made of instead of a flat rule. Same pairing rule as above:
   Visitar's bottom-right vertex is Fazer's bottom-left, and so on. Deepest
   value here must stay <= --jigsaw-foot. */
.bento-container > .bento-visitar   { --cut-l: 40px; --cut-r: 14px; --cut-bl: 12px; --cut-br: 34px; }
.bento-container > .bento-fazer     { --cut-l: 14px; --cut-r: 36px; --cut-bl: 34px; --cut-br: 10px; }
.bento-container > .bento-bus       { --cut-l: 36px; --cut-r: 10px; --cut-bl: 10px; --cut-br: 30px; }

.bento-container:not(.sidebar-mode) > .bento-item {
  /* One expression covers both directions: each edge simply runs from
     whatever height the tile's left corner sits at to its right one, so it
     slopes down or up depending on the pair. */
  clip-path: polygon(
    0 var(--cut-l),
    100% var(--cut-r),
    100% calc(100% - var(--cut-br)),
    0 calc(100% - var(--cut-bl))
  );
  /* Flat, hard-edged surfaces: a radius fights the clip-path and a shadow
     is invisible once clipped. */
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
  /* bento-grid.css floors tiles at 220px (260px above 1400px wide) for the
     old scrolling grid; here the three rows must divide the viewport. */
  min-height: 0;
  align-items: center;
}

/* Rows 2-3 ride up over the row above so their diagonal top edge forms the
   seam rather than exposing the page background through it. Rows are laid
   out in DOM order, so a later row already paints on top. */
.bento-container:not(.sidebar-mode) > .bento-ficar,
.bento-container:not(.sidebar-mode) > .bento-comer,
.bento-container:not(.sidebar-mode) > .bento-agenda,
.bento-container:not(.sidebar-mode) > .bento-visitar,
.bento-container:not(.sidebar-mode) > .bento-fazer,
.bento-container:not(.sidebar-mode) > .bento-bus {
  margin-top: calc(var(--jigsaw-overlap) * -1);
}

/* Icon centred above a centred label, as in the original - bento-grid.css
   pins the pair to the bottom-left corner instead. The extra top padding
   offsets the diagonal bite out of the tile's top edge so the pair still
   reads as optically centred. */
.bento-container:not(.sidebar-mode) .bento-content {
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: calc(clamp(12px, 1.4vh, 26px) + var(--cut)) 20px clamp(12px, 1.4vh, 26px);
}

/* Rows 1-2 have the next row riding up over their bottom edge, so their
   last --jigsaw-overlap pixels are hidden. Pad that strip out too, or the
   label ends up centred on the tile's box rather than on the part of it
   that's actually visible. */
.bento-container:not(.sidebar-mode) > .bento-municipio .bento-content,
.bento-container:not(.sidebar-mode) > .bento-noticias .bento-content,
.bento-container:not(.sidebar-mode) > .bento-ficar .bento-content,
.bento-container:not(.sidebar-mode) > .bento-comer .bento-content,
.bento-container:not(.sidebar-mode) > .bento-agenda .bento-content {
  padding-bottom: calc(clamp(12px, 1.4vh, 26px) + var(--jigsaw-overlap));
}

/* Same correction for the bottom row, whose visible area is now shortened by
   the slant cut out of its bottom edge instead. */
.bento-container:not(.sidebar-mode) > .bento-visitar .bento-content,
.bento-container:not(.sidebar-mode) > .bento-fazer .bento-content,
.bento-container:not(.sidebar-mode) > .bento-bus .bento-content {
  padding-bottom: calc(clamp(12px, 1.4vh, 26px) + var(--jigsaw-foot));
}

.bento-container:not(.sidebar-mode) .bento-icon {
  margin-bottom: clamp(8px, 1.5vh, 24px);
}

.bento-container:not(.sidebar-mode) .bento-icon img {
  /* Original: a flat 70px tall on a 1080px-wide canvas, which 6.4vw matches
     at that width. Sized by height with width:auto so the flight can animate
     one dimension and let the other follow. */
  height: clamp(44px, 6.4vw, 84px);
  width: auto;
}

.bento-container:not(.sidebar-mode) .bento-title {
  font-family: inherit;
  /* The original's 1.4em on a 1080px canvas, scaled up: its tiles were
     ~350px tall against ~480px here, so the label needs the extra size to
     hold the middle of the tile. */
  font-size: clamp(1.1rem, 2.6vw, 2.4rem);
  font-weight: 300;
  letter-spacing: 0.04em;
  line-height: 1.25;
  text-transform: uppercase;
  text-shadow: none;
}

/* bento-grid.css lifts and shadows tiles on hover and drops the overlay to
   80% opacity. Lifting breaks the interlocking seams, and with solid fills
   the fade just reveals the page background, so both are dropped for a
   press-state brighten - this is a touch screen, where :hover also sticks
   after a tap. */
.bento-container:not(.sidebar-mode) > .bento-item:hover {
  transform: none;
  box-shadow: none;
}

.bento-container:not(.sidebar-mode) > .bento-item:hover .bento-overlay {
  opacity: 1;
}

.bento-container:not(.sidebar-mode) > .bento-item:hover .bento-icon,
.bento-container:not(.sidebar-mode) > .bento-item:hover .bento-title {
  transform: none;
}

.bento-container:not(.sidebar-mode) > .bento-item:active .bento-overlay {
  filter: brightness(1.18);
}

/* Sidebar buttons: undo the mosaic so the tiles can stack as a single column
   of icon buttons. Corners stay hard - the old kiosk had no rounded geometry
   anywhere, so a tile morphing from a sharp mosaic panel into a rounded square
   mid-transition reads as a glitch. bento-grid.css sets the 16px radius with
   !important, and bentoAnimation.js also animates borderRadius inline, so that
   value is zeroed there too.

   The buttons keep their cut rather than collapsing to plain squares, so the
   rail still reads as cut from the same folded surface as the mosaic it came out
   of - but each one is cut its own way (see below) rather than all eight sharing
   a shear, which made the rail read as one striped column, and each cut is
   shared with the neighbour it faces so the gaps stay even. */
.bento-item.sidebar-state {
  grid-area: auto !important;
  margin: 0 !important;
  border-radius: 0 !important;
  clip-path: var(--rail-cut, polygon(0% 6%, 100% 0%, 100% 94%, 0% 100%)) !important;
}

/* A shape per button, cut as though one strip had been sliced across at a
   different angle each time: each cut is shared by the two buttons that meet
   along it, so a button's bottom edge runs parallel to the top edge of the one
   below and the gap between them stays an even stripe. Read down the pairs
   below and every button's second Y matches the next button's first.

   Percentages, because the flight has to tween the mosaic polygon into these; a
   shape it can't express in the same units would snap on at the end of the
   transition instead. These are generated from SIDEBAR_EDGE_TILTS in
   bentoAnimation.js and have to stay in step with it, or a tile will change
   shape at the moment the animation hands it back to the stylesheet.

   They're held as a custom property rather than set on clip-path directly so the
   active marker further down can be cut from the same shape as the button it
   sits on. A tile with no cut of its own falls back to the shape in the var()
   above, which matches SIDEBAR_CLIP_FALLBACK. */
.bento-item.bento-municipio { --rail-cut: polygon(0% 6%, 100% 0%, 100% 100%, 0% 92%); }
.bento-item.bento-noticias  { --rail-cut: polygon(0% 0%, 100% 8%, 100% 95%, 0% 100%); }
.bento-item.bento-agenda    { --rail-cut: polygon(0% 5%, 100% 0%, 100% 100%, 0% 91%); }
.bento-item.bento-ficar     { --rail-cut: polygon(0% 0%, 100% 9%, 100% 93%, 0% 100%); }
.bento-item.bento-comer     { --rail-cut: polygon(0% 7%, 100% 0%, 100% 100%, 0% 94%); }
.bento-item.bento-visitar   { --rail-cut: polygon(0% 0%, 100% 6%, 100% 92%, 0% 100%); }
.bento-item.bento-fazer     { --rail-cut: polygon(0% 8%, 100% 0%, 100% 100%, 0% 93%); }
.bento-item.bento-bus       { --rail-cut: polygon(0% 0%, 100% 7%, 100% 95%, 0% 100%); }

/* Which section is open. The marker sits on the side the content panel opens
   from, and is cut from the button's own shape: a small slab of the same
   quadrilateral, so its ends slope the way that button's top and bottom edges
   do - leaning the same way, only steeper for being nine pixels wide. Nothing
   here is per-button; --rail-cut carries the angles.

   It stays a margin clear of all four edges. Flush to the right edge it would
   have read as a notch taken out of the button instead of a mark on it, white
   against the near-white page being no edge at all.

   What was here was a rounded 4px capsule floating mid-tile, unfolding from a
   dot under a white glow and then pulsing on a two-second loop, plus a glow
   around the button itself: soft, round and animated forever, where the rest of
   the app is flat, cut and still once it arrives. The radius survived the
   blanket reset below because that covers the panel, the sidebar content and the
   footer - not the rail. */
.bento-item.sidebar-state.active::after {
  content: '';
  position: absolute;
  right: 11px;
  left: auto;
  top: 13px;
  bottom: 13px;
  width: 9px;
  height: auto;
  background: #fff;
  border-radius: 0;
  box-shadow: none;
  clip-path: var(--rail-cut, polygon(0% 6%, 100% 0%, 100% 94%, 0% 100%));
  transform: none;
  animation: railMarkIn 0.26s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}

.bento-item.sidebar-state.active.shrinking::after {
  /* Inside the 400ms updateActiveStates() waits before dropping the classes. */
  animation: railMarkOut 0.2s cubic-bezier(0.4, 0, 0.8, 0.4) forwards;
}

/* In from the edge it points at, rather than growing out of nothing. */
@keyframes railMarkIn {
  from { opacity: 0; transform: translateX(9px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes railMarkOut {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(9px); }
}

/* The active button carried an outer and an inset white glow to match the old
   indicator's. Nothing else in the app glows. */
.bento-item.sidebar-state.active {
  box-shadow: none;
}

/* The state a tile is in while GSAP carries it between the mosaic and the
   sidebar (see addTileFlight() in base/static/js/bentoAnimation.js). The
   .sidebar-state rules in bento-grid.css can't be used for this: they pin the
   tile to 100x100 and its content to a centred 40px glyph with !important,
   which would make the size, padding and label snap at the start of the
   flight instead of tweening across it. Here the tile is simply released from
   the grid and everything else is left for the timeline to interpolate. */
.bento-item.tile-flying,
.bento-item.tile-flying .bento-content,
.bento-item.tile-flying .bento-background,
.bento-item.tile-flying .bento-icon,
.bento-item.tile-flying .bento-icon img,
.bento-item.tile-flying .bento-title,
.bento-item.tile-flying .bento-subtitle {
  /* bento-grid.css puts `transition: all 0.3s` on the tile, its icon and its
     label for the hover states. Left on, the browser eases towards every
     value the timeline sets, so the tile trails a third of a second behind
     its own animation and arrives late - which is what made the tiles look
     like they jumped at the end of the transition rather than landing. */
  transition: none !important;
}

.bento-item.tile-flying {
  position: fixed;
  z-index: 200;
  grid-area: auto !important;
  margin: 0 !important;
  border-radius: 0 !important;
  min-width: 0 !important;
  max-width: none !important;
  min-height: 0 !important;
  max-height: none !important;
  overflow: hidden;
}

.bento-item.tile-flying .bento-content {
  display: flex !important;
  flex-direction: column;
  align-items: center !important;
  justify-content: center !important;
  text-align: center;
  width: 100% !important;
  height: 100% !important;
}

/* The label keeps its place in the layout the whole way across, shrinking to
   nothing rather than being switched off, so the icon above it travels on one
   continuous path instead of hopping when the label leaves the flow. */
.bento-item.tile-flying .bento-title,
.bento-item.tile-flying .bento-subtitle {
  display: block !important;
}

.bento-item.tile-flying .bento-background {
  display: block !important;
}

.bento-item.tile-flying .bento-icon {
  transform: none !important;
}

/* bento-grid.css blows the sidebar glyph up to 40px scaled 1.4x - 56px in a
   100px button, which crowds it. These icons also carry more detail than the
   solid ones the sizing was tuned for, so they need the extra breathing
   room. */
.bento-item.sidebar-state .bento-icon {
  transform: none !important;
}

.bento-item.sidebar-state .bento-icon img {
  width: 46px !important;
  height: 46px !important;
}

/* Same reasoning for the panel the category content slides into. */
.content-panel {
  border-radius: 0;
}

/* Category listing cards (Onde Ficar/Comer/Visitar) take the same language as
   the mosaic: a single slanted top edge, as every tile in the original kiosk
   had, at a shallower depth to suit their smaller footprint. Only the top edge
   is cut - these cards end on a white text block, so a matching slant along the
   bottom would be cutting white out of white and never read.

   The radius has to go at the same time. style.css rounds all four corners, and
   clipping two of them left each card with a rounded corner facing a cut one
   diagonally across from it, which is what made them look unrelated to
   everything around them.

   Listing cards keep their real POI photo (that photo *is* the differentiating
   content between e.g. two hotels), unlike the home tiles above which are pure
   category navigation. */
.sidebar-item {
  --card-cut: 14px;
  clip-path: polygon(0 var(--card-cut), 100% 0, 100% 100%, 0 100%);
  border-radius: 0 !important;
}

/* The Município cards (Sobre o Concelho, Editais) are navigation, not content:
   they sit in the sticky bar at the top of the page and do the same job as the
   back/home/filter buttons, which are all plain rectangles. Slanted, they read
   as two more content tiles that happen to be small, and the cut only showed
   against the white bar behind them as a nick out of the top corner. */
.municipio-card {
  clip-path: none;
  border-radius: 0 !important;
}

/* The photo inside is rounded separately, which would leave a stray curve
   sitting inside the cut. */
.sidebar-item-image,
.sidebar-item-image img,
.municipio-card-image,
.municipio-card-image img {
  border-radius: 0 !important;
}

/* Município cards (Sobre o Concelho, Editais) are pure navigation links, not
   content-differentiated like a POI listing card - solid colour + icon instead
   of a generic stock photo, same rationale as the home tiles.

   Colour runs the full height of the card, label included. style.css builds
   these as a photo above a white caption block, which on a solid tile read as a
   red panel with a white bar stuck to the bottom of it - the icon and its label
   are one thing and belong on one surface. Both the fill and the white type are
   set on the card, so the caption block just has to stop painting over it. */
.municipio-card-solid {
  background: linear-gradient(135deg, var(--lagoa-red) 0%, var(--lagoa-red-dark) 100%);
}

.municipio-card-solid .municipio-card-content {
  background: transparent;
}

.municipio-card-solid .municipio-card-title {
  color: #fff;
}

.municipio-card-solid .municipio-card-image {
  display: flex;
  align-items: center;
  justify-content: center;
}

.municipio-card-solid .municipio-card-icon {
  width: 64px;
  height: 64px;
  object-fit: contain;
  /* The Material Symbols set ships black-filled; every other place these
     icons appear (tiles, placeholders) inverts them the same way. */
  filter: brightness(0) invert(1);
}

/* --- Chrome accents (sidebar/splash/municipio) ---
   style.css hardcodes Moncorvo's dark navy (#17243d) as the *generic*
   default for sidebar titles/buttons, splash screen, and "municipio"
   card/contact titles. Re-point the generic default at the Lagoa teal.
   Deliberately no !important: style.css also defines more specific
   per-section overrides (e.g. `.sidebar-title.noticias`, `.sidebar-content
   .municipio .back-button`) for category color differentiation - those
   must keep winning by specificity, since lagoa-theme.css only needs to
   beat the equal-specificity generic rule by loading after it. */
.sidebar-title,
.municipio-card-title,
.municipio-contacts-title,
.municipio-contact-title {
  color: var(--lagoa-teal);
}

.back-button,
.home-button,
.splash,
.municipio-contacts-carousel .owl-nav button:hover {
  background: var(--lagoa-teal);
}

/* --- Header logo ---
   style.css applies `filter: brightness(0) invert(1)` to .logo-header,
   which was needed to turn Moncorvo's dark logo white for the dark
   header. Lagoa's logo is already full-color with its own white backing
   shape (Fill-1 in lagoa_logo.svg), so that filter crushes every color
   to solid white. Undo it here. */
.top-header .logo-header {
  filter: none;
}

/* --- Município main grid ---
   style.css hardcodes a 3-column grid for the Presidente/Freguesias/Editais
   cards. Lagoa only has real content for two of those three (Sobre o Concelho +
   Editais, the dead-content tiles having been dropped), so the 3-column grid
   would otherwise leave an empty column of blank
   space.

   Two full columns rather than two 360px cards: this grid and the contact
   category tiles below it share a 20px gap and the same 40px right inset (both
   in style.css), so filling the width lands the two cards exactly on the
   columns of the tiles underneath. */
.municipio-main-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* The title and the two section links stay in view while the contacts scroll
   past them, so both remain reachable from anywhere in a list ninety-odd
   cards long.

   The header is wrapped in here with them rather than being stuck separately:
   it's already `position: sticky; top: 0` in style.css, and nesting it inside
   another sticky box changes nothing about where it lands, while sticking the
   links on their own would mean repeating the header's height here as an
   offset and keeping the two in step by hand.

   The white background does real work - the links sit in a grid with a gap
   between them, and without it the contacts would be seen sliding through
   that gap and around the cards. */
.municipio-sticky {
  position: sticky;
  top: 0;
  z-index: 11;
  background: #fff;
  /* Reads as a layer the list passes under, rather than as cards that happen
     to be cut off at that line. */
  box-shadow: 0 8px 12px -8px rgba(0, 0, 0, 0.15);
}

/* A sticky child can only travel as far as its containing block, and
   style.css pins .sidebar-content to exactly the panel's height - so the bar
   came unstuck about a screenful down, which is precisely where the contacts
   list gets going. Growing with its content is what a page that scrolls wants
   anyway. Scoped to this page: the other sections' inner scrolling is built on
   that fixed height. */
.sidebar-content.municipio {
  height: auto;
  min-height: 100%;
}

/* Compact enough to live in a bar that's always on screen: the cards were
   sized as the page's main content, which they no longer are. */
.municipio-card {
  height: 200px;
}

.municipio-card-image {
  height: 120px;
}

/* --- Município contacts ---
   These were an Owl carousel: three cards on a horizontal track, which left
   the bottom half of the panel empty and squeezed every contact into a fixed
   230px box - long addresses were simply clipped, and the text had to sit at
   ~13px to fit at all. A plain grid fills the panel, scrolls with the rest of
   the page, and gives the text room to be read from standing distance. */
.municipio-contacts-grid {
  display: grid;
  /* Two columns in the panel as it stands (~780px of usable width); the
     minimum is what decides it, so a wider panel gains a third rather than
     stretching two across the whole width. */
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}

/* Height comes from the content now, with the grid row keeping neighbours
   level; the margin was spacing for the carousel track, which the gap
   handles. */
.municipio-contact-card {
  height: auto;
  margin: 0;
  padding: 22px 24px;
}

/* --- Contact category tiles ---
   The Município page shows the categories (Saúde, Táxis, Cemitérios, ...) and
   the contacts themselves live one level down, in contactos_categoria.html:
   fifty-odd cards on one page meant scrolling past a cemetery and a dentist to
   reach a taxi, with no way to tell from the top what was down there.

   These are content, not chrome - the page's own subject, like the listing cards
   in the other sections - so they take the slanted top edge those have, and the
   section's red. */
.contact-category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}

.contact-category-card {
  --card-cut: 14px;
  clip-path: polygon(0 var(--card-cut), 100% 0, 100% 100%, 0 100%);
  display: flex;
  align-items: center;
  gap: 20px;
  /* Room for the cut to eat into above the icon without moving it. */
  padding: 26px 26px 20px;
  background: linear-gradient(135deg, var(--lagoa-red) 0%, var(--lagoa-red-dark) 100%);
  text-decoration: none;
}

.contact-category-icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
}

.contact-category-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* The Material Symbols set ships black-filled, as everywhere else these
     icons sit on a brand colour. */
  filter: brightness(0) invert(1);
}

.contact-category-title {
  margin: 0;
  color: #fff;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.25;
}

.contact-category-count {
  display: block;
  margin-top: 6px;
  color: rgba(255, 255, 255, 0.8);
  font-size: 0.95rem;
}

.municipio-contact-title {
  font-size: 1.2rem;
}

.municipio-contact-label {
  font-size: 0.85rem;
}

.municipio-contact-value {
  font-size: 1.05rem;
  line-height: 1.45;
}

/* ==========================================
   NEWS LIST
   Headlines here are long, all-caps sentences - CMS copy, not something that
   can be trimmed at this end. In style.css's three-column grid of square
   cards they wrapped to four and five lines and took over the card, leaving
   the photo as a strip along the top.

   One story per row instead, photo left, headline and date right: the same
   headline gets four times the line length, so it wraps to two lines and the
   photo keeps a usable size. Scoped to .noticias - the other sections list
   places, whose names are short and whose photo is the thing being chosen
   between, so the square cards suit them.

   These rules fight a wall of !important in style.css and have to out-specify
   it as well as match it, hence .sidebar-content.noticias on everything.
========================================== */

.sidebar-content.noticias .sidebar-grid {
  grid-template-columns: 1fr !important;
  row-gap: 16px;
}

.sidebar-content.noticias .sidebar-grid .sidebar-item {
  display: grid !important;
  grid-template-columns: 240px 1fr;
  /* The square card is what forced the headline into a narrow column; the row
     takes its height from the photo instead. */
  aspect-ratio: auto !important;
  align-items: stretch;
  min-height: 150px !important;
}

/* The photo is taken out of the height calculation - absolutely positioned, so
   it contributes no intrinsic height and can't set the row's. Left to itself a
   portrait photo stretched its row to 370px while its neighbours sat at 145px.
   Now the headline sets the height, the row floor keeps it from collapsing on
   short ones, and the photo stretches to whatever that comes to. */
.sidebar-content.noticias .sidebar-item-image {
  height: auto;
  flex: none;
  position: relative;
}

.sidebar-content.noticias .sidebar-item-image img,
/* The placeholder shown for a story with no CMS photo has to be taken out of
   the height calculation the same way, or its height:100% resolves against an
   auto-height parent, collapses to nothing, and the headline slides left into
   the photo column. */
.sidebar-content.noticias .sidebar-item-image .placeholder-image {
  position: absolute !important;
  inset: 0;
  width: 100%;
  height: 100% !important;
  object-fit: cover;
}

.sidebar-content.noticias .sidebar-item-content {
  justify-content: center !important;
  gap: 10px !important;
  padding: 18px 26px !important;
  text-align: left;
}

/* Three lines, then an ellipsis - the full headline is on the story page. Only
   a couple of the sixty need it, and without the cap they overflow their row:
   the row track is sized in an intrinsic pass where the text hasn't wrapped to
   its final width yet, so a long headline's fourth line spills past the row
   the grid reserved for it. */
.sidebar-content.noticias h5.sidebar-item-text {
  font-size: 20px !important;
  line-height: 26px !important;
  color: var(--lagoa-brown);
  display: -webkit-box !important;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden !important;
}

.sidebar-content.noticias .sidebar-item-date {
  font-size: 15px;
  color: var(--lagoa-teal);
  background: rgba(var(--lagoa-teal-rgb), 0.07);
}

/* ==========================================
   CATEGORY COLOUR SYSTEM
   Every section's chrome takes the colour of the home tile it was opened
   from, so a category keeps one identity from the menu through to its
   content: Município red, Notícias teal, Agenda/Onde Ficar green, Onde
   Comer/Transportes orange, O Que Visitar teal, O Que Fazer brown.

   style.css carries Moncorvo's unrelated palette for these (purple #9333ea
   for Onde Ficar, amber for Agenda, and #a13854 - Moncorvo's burgundy - for
   everything under Município). Those live in ~40 rules spread over the
   file, so they're re-pointed here in one place rather than edited in situ.
   The tile colours themselves are in the .bento-*-overlay block above, and
   bentoAnimation.js repeats them for the collapse animation - all three
   need to agree.
========================================== */

.sidebar-title.municipio { color: var(--lagoa-red); }
.sidebar-title.noticias { color: var(--lagoa-teal); }
.sidebar-title.agenda { color: var(--lagoa-green); }
.sidebar-title.ficar { color: var(--lagoa-green); }
.sidebar-title.comer { color: var(--lagoa-orange); }
.sidebar-title.visitar { color: var(--lagoa-teal); }
.sidebar-title.fazer { color: var(--lagoa-brown); }
.sidebar-title.bus { color: var(--lagoa-orange); }
.sidebar-title.editais { color: var(--lagoa-red); }
.sidebar-title.roteiros { color: var(--lagoa-teal); }

.sidebar-content.municipio .back-button,
.sidebar-content.municipio .home-button { background: var(--lagoa-red); }
.sidebar-content.noticias .back-button,
.sidebar-content.noticias .home-button { background: var(--lagoa-teal); }
.sidebar-content.agenda .back-button,
.sidebar-content.agenda .home-button { background: var(--lagoa-green); }
.sidebar-content.ficar .back-button,
.sidebar-content.ficar .home-button { background: var(--lagoa-green); }
.sidebar-content.comer .back-button,
.sidebar-content.comer .home-button { background: var(--lagoa-orange); }
.sidebar-content.visitar .back-button,
.sidebar-content.visitar .home-button { background: var(--lagoa-teal); }
.sidebar-content.fazer .back-button,
.sidebar-content.fazer .home-button { background: var(--lagoa-brown); }
.sidebar-content.bus .back-button,
.sidebar-content.bus .home-button { background: var(--lagoa-orange); }
.sidebar-content.editais .back-button,
.sidebar-content.editais .home-button { background: var(--lagoa-red); }
.sidebar-content.roteiros .back-button,
.sidebar-content.roteiros .home-button { background: var(--lagoa-teal); }

/* ==========================================
   LIST FILTERS
   The sub-category chips above O Que Visitar's and O Que Fazer's lists. The
   markup they replaced (.topmenu > .filter > ul) was a Moncorvo leftover with
   no stylesheet on this side at all, so it rendered as three full-width list
   items - and .filter already means something else here (the gradient wash
   over photo cards in style.css), which is why these are renamed rather than
   styled in place.

   Right-aligned to sit over the list they filter, matching the 40px right
   padding of .sidebar-header and .sidebar-grid. Each section tints them with its
   own colour.
========================================== */

.sidebar-content.visitar { --filter-accent: var(--lagoa-teal); --filter-accent-rgb: var(--lagoa-teal-rgb); }
.sidebar-content.fazer { --filter-accent: var(--lagoa-brown); --filter-accent-rgb: var(--lagoa-brown-rgb); }

.list-filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  padding: 18px 40px 0 0;
  flex-shrink: 0;
}

/* Square, like the rest of the navigation - the cut shapes are for the tiles and
   cards, not for the controls that sit over them. */
.list-filter {
  padding: 13px 22px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--filter-accent, var(--lagoa-brown));
  background: rgba(var(--filter-accent-rgb, var(--lagoa-brown-rgb)), 0.09);
  transition: background 0.2s ease, color 0.2s ease;
}

.list-filter.is-active {
  color: #fff;
  background: var(--filter-accent, var(--lagoa-brown));
}

/* The list normally clears the header by 60px; with chips in between that
   reads as a gap the chips are floating in, so it tightens up. */
.sidebar-content:has(.list-filters) .sidebar-grid {
  padding-top: 18px;
}

/* Photo placeholders shown for items with no CMS image. */
.placeholder-municipio,
.placeholder-editais {
  background: linear-gradient(135deg, var(--lagoa-red) 0%, var(--lagoa-red-dark) 100%);
}
.placeholder-noticias,
.placeholder-visitar,
.placeholder-roteiros {
  background: linear-gradient(135deg, var(--lagoa-teal) 0%, var(--lagoa-teal-dark) 100%);
}
.placeholder-agenda,
.placeholder-ficar {
  background: linear-gradient(135deg, var(--lagoa-green) 0%, var(--lagoa-green-dark) 100%);
}
.placeholder-comer {
  background: linear-gradient(135deg, var(--lagoa-orange) 0%, var(--lagoa-orange-dark) 100%);
}
.placeholder-fazer {
  background: linear-gradient(135deg, var(--lagoa-brown) 0%, var(--lagoa-brown-dark) 100%);
}

/* ...and their glyphs, onto the same icon set the menu tiles use. */
.placeholder-municipio .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_municipio.svg"); }
.placeholder-editais .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_editais.svg"); }
.placeholder-noticias .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_noticias.svg"); }
.placeholder-agenda .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_agenda.svg"); }
.placeholder-ficar .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_ficar.svg"); }
.placeholder-comer .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_comer.svg"); }
.placeholder-visitar .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_visitar.svg"); }
.placeholder-fazer .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_fazer.svg"); }
.placeholder-roteiros .placeholder-icon { background-image: url("/static/img/icones/lagoa/m_roteiros.svg"); }

/* Município sub-pages: contact cards and the PDF viewer used for Editais -
   both reached through the Município tile, so both red. */
.municipio-contact-label {
  color: var(--lagoa-red);
}

.pdf-icon,
.pdf-button,
.pdf-download-btn,
.municipio-contact-card::before {
  background: linear-gradient(135deg, var(--lagoa-red) 0%, var(--lagoa-red-dark) 100%);
}

/* The fill is on the card itself now (see .municipio-card-solid above); left
   here, this block's own gradient would run its full 135deg across the top 120px
   and meet the card's at a visible line under the icon. */
.municipio-card-solid .municipio-card-image {
  background: transparent;
}

/* ==========================================
   WHITE BACKGROUND THEME
   style.css/bento-grid.css were built around a dark navy body
   (a cover photo behind a dark gradient) with white header/footer text and
   icons. The agreed Lagoa design direction is a white/light background
   with solid brand-colour tiles (see menu-structure-draft.md), so the
   global chrome needs flipping - the tiles themselves (.bento-overlay,
   .bento-icon, .bento-title) stay white-on-colour and need no changes,
   since they still sit on solid saturated brand colours, not on the body.
========================================== */

body[data-barba="wrapper"] {
  background: #FAFAF8;
}

/* The dark gradient overlay existed to darken that photo for contrast under
   white text - with a plain light body there's nothing left to darken. */
body[data-barba="wrapper"]::before {
  background: none;
}

/* Header: was a transparent-to-dark-navy fade with hardcoded white text,
   designed to sit on the dark body. Flip to a light fade with dark text. */
.top-header {
  background: linear-gradient(0deg, rgba(255, 255, 255, 0) 0%, rgba(250, 250, 248, 0.92) 100%);
}

.top-header .hora-data,
.top-header .meteo-uv,
.top-header .meteo-uv .uv {
  color: var(--lagoa-teal);
}

/* ==========================================
   FOOTER — mosaic-aligned chrome
   Zig-zag top mates the mosaic foot cuts (visitar / fazer / bus). Section
   dividers run from those join corners down past the shutter. Idle = no
   chrome; .active = soft trapezoid wash. Shutter = rounded trapezoid mask.
========================================== */

.main-footer {
  --footer-h: 168px;
  box-sizing: border-box;
  height: var(--footer-h);
  min-height: var(--footer-h);
  padding: 38px 16px 16px;
  background: #ecebe9;
  backdrop-filter: none;
  align-items: stretch;
  gap: 0;
  z-index: 40;
  /* Exact mate to mosaic --cut-bl/--cut-br on the bottom row (max cut 34px). */
  clip-path: polygon(
    0% 13.1%,
    33.33% 0%,
    66.67% 14.29%,
    100% 2.38%,
    100% 100%,
    0% 100%
  );
}

/* V dividers on the footer itself (not the shorter sections) so they span
   the full band — top clipped to the zig-zag vertices, bottom to the edge. */
.main-footer::before,
.main-footer::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1.5px;
  background: rgba(var(--lagoa-brown-rgb), 0.32);
  pointer-events: none;
  z-index: 2;
}

/* Left join → \ */
.main-footer::before {
  left: 33.333%;
  transform: skewX(14deg);
  transform-origin: top center;
}

/* Right join → / */
.main-footer::after {
  left: 66.667%;
  transform: skewX(-14deg);
  transform-origin: top center;
}

.footer-section {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  padding: 10px 8px 0;
  z-index: 1;
}

.footer-left {
  justify-content: center;
}

.footer-center {
  /* Exact third so the shutter sits between the column joins. */
  flex: 0 0 33.333%;
  width: 33.333%;
  max-width: none;
  padding-top: 6px;
}

.footer-right {
  justify-content: center;
}

/* Groups are invisible containers — no fill, no clip, no border when idle. */
.unified-language-toggle,
.unified-accessibility-toggle {
  display: flex;
  align-items: stretch;
  justify-content: center;
  width: 100%;
  max-width: 400px;
  min-height: 0;
  background: transparent;
  border: none;
  border-radius: 0;
  backdrop-filter: none;
  overflow: visible;
  transform: none;
  clip-path: none;
  gap: 0;
}

.unified-language-toggle:hover,
.unified-accessibility-toggle:hover {
  background: transparent;
  border: none;
  transform: none;
}

.unified-lang-btn,
.unified-accessibility-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  min-height: 84px;
  padding: 14px 18px;
  border: none;
  background: transparent;
  color: rgba(var(--lagoa-brown-rgb), 0.72);
  text-decoration: none;
  box-shadow: none;
  transition: background 0.2s ease, color 0.2s ease;
}

.unified-lang-btn {
  flex-direction: row;
  gap: 14px;
}

.footer-btn-label {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: inherit;
  line-height: 1.15;
  text-align: center;
}

.unified-lang-btn .footer-btn-label {
  font-size: 20px;
  font-weight: 300;
  letter-spacing: 0.02em;
}

.unified-lang-btn img {
  width: 42px;
  height: 42px;
  border-radius: 50% !important;
  object-fit: cover;
  flex-shrink: 0;
  box-shadow: none;
}

.unified-accessibility-btn img {
  width: 36px;
  height: 36px;
  filter: none;
}

/* Idle must fully clear — :focus stays after a tap on kiosk Chrome and was
   leaving a faint grey square that looked like a stuck active state. */
.unified-accessibility-btn:not(.active),
.unified-accessibility-btn:not(.active):hover,
.unified-accessibility-btn:not(.active):focus,
.unified-accessibility-btn:not(.active):focus-visible,
.unified-accessibility-btn:not(.active):active,
.unified-accessibility-btn:not(.active):visited {
  background: transparent;
  box-shadow: none;
  outline: none;
  clip-path: none;
  border: none;
}

/* Language: no trapezoid — active = colour flag + stronger label; idle = B&W. */
.unified-lang-btn {
  background: transparent;
  clip-path: none;
}

.unified-lang-btn:hover {
  background: transparent;
  color: var(--lagoa-brown);
}

.unified-lang-btn img {
  filter: grayscale(1);
  opacity: 0.55;
  transition: filter 0.2s ease, opacity 0.2s ease;
}

.unified-lang-btn .footer-btn-label {
  font-weight: 300;
  transition: color 0.2s ease, font-weight 0.2s ease;
}

.unified-lang-btn.active {
  background: transparent;
  box-shadow: none;
  color: var(--lagoa-brown-dark);
  clip-path: none;
}

.unified-lang-btn.active img {
  filter: none;
  opacity: 1;
}

.unified-lang-btn.active .footer-btn-label {
  font-weight: 600;
}

/* A11y / contrast: soft trapezoid wash while toggled (temporary use). */
.unified-accessibility-btn.active {
  background: rgba(var(--lagoa-brown-rgb), 0.14);
  box-shadow: none;
  color: var(--lagoa-brown-dark);
}

.unified-accessibility-btn.left.active {
  clip-path: polygon(0% 10%, 100% 0%, 100% 100%, 0% 90%);
}

.unified-accessibility-btn.right.active {
  clip-path: polygon(0% 0%, 100% 10%, 100% 90%, 0% 100%);
}

.unified-accessibility-btn.active .footer-btn-label {
  font-weight: 500;
}

.lang-separator,
.accessibility-separator {
  display: none;
}

/* Shutter: rounded trapezoid via SVG mask (wider at top). */
.selfie-btn {
  width: 160px;
  height: 92px;
  padding: 0;
}

/* ==========================================
   AUTOCARROS
   Under-map chrome is a quiet tool surface (not callout slabs / admin
   table headers). Section orange is reserved for primary actions and step
   markers. Markup ids stay a contract with busmap.js.
========================================== */

.bus-body {
  flex: 1;
  overflow-y: auto;
  /* Right padding only, matching .sidebar-grid: the panel supplies the left
     inset, and the gap keeps content clear of the scrollbar. */
  padding: 24px 40px 60px 0;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.bus-body::-webkit-scrollbar {
  display: none;
}

.bus-intro {
  margin: 0 0 22px;
  font-size: 20px;
  line-height: 1.5;
  color: var(--lagoa-brown);
}

.bus-map-wrap {
  position: relative;
  /* Single hairline under the map so search / result / error don't stack borders. */
  border-bottom: 1px solid rgba(var(--lagoa-brown-rgb), 0.12);
}

.mapa.bus {
  width: 100%;
  height: 420px;
}

/* Sits over the map's top-left corner, clear of Google's own controls, which
   take the right side and the bottom. Orange rather than the white it used to
   be: the maximise glyph is a white SVG, so on white it was an empty square. */
.bus-map-toggle {
  position: absolute;
  top: 16px;
  left: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 54px;
  height: 54px;
  padding: 0;
  border: none;
  background: var(--lagoa-orange);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}

.bus-map-toggle:hover {
  background: var(--lagoa-orange-dark);
}

.bus-map-toggle img {
  width: 24px;
  height: 24px;
}

/* --- Destination search --- */
/* Input + CTA share one edge under the map (map chrome, not a floating form). */

.bus-search {
  display: flex;
  gap: 0;
  margin-top: 0;
  padding-top: 18px;
}

/* Tall enough to be a comfortable touch target, and to sit level with the
   button beside it. */
.bus-search-input {
  flex: 1;
  height: 74px;
  padding: 0 24px;
  border: 1px solid rgba(var(--lagoa-brown-rgb), 0.18);
  border-right: none;
  background: #fff;
  color: var(--lagoa-brown);
  font-size: 22px;
}

.bus-search-input::placeholder {
  color: #9a938c;
}

.bus-search-input:focus {
  outline: none;
  border-color: var(--lagoa-orange);
  /* Keep the attached CTA edge clean while the field is focused. */
  border-right: none;
  box-shadow: inset 0 0 0 1px var(--lagoa-orange);
}

.bus-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  height: 74px;
  padding: 0 34px;
  border: none;
  background: var(--lagoa-orange);
  color: #fff;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
}

.bus-btn:hover {
  background: var(--lagoa-orange-dark);
}

.bus-btn img {
  width: 22px;
  height: 22px;
  /* Search glyph is a black SVG; the primary button is orange. */
  filter: brightness(0) invert(1);
}

/* Secondary action (Nova Pesquisa) — same language as .list-filter chips. */
.bus-btn.bus-btn-secondary {
  height: auto;
  min-height: 54px;
  padding: 13px 22px;
  background: rgba(var(--lagoa-orange-rgb), 0.12);
  color: var(--lagoa-orange-dark);
  font-size: 14px;
}

.bus-btn.bus-btn-secondary:hover {
  background: var(--lagoa-orange);
  color: #fff;
}

.bus-btn.bus-btn-secondary img {
  width: 18px;
  height: 18px;
  filter: none;
  /* Glyph is black; tint toward section orange. */
  opacity: 0.75;
}

.bus-btn.bus-btn-secondary:hover img {
  filter: brightness(0) invert(1);
  opacity: 1;
}

/* Replaces the search row once a route is found - busmap.js flips the two,
   setting display:flex, which is why only the axis properties are set here.
   Quiet typography row — no left accent bar, no tinted callout slab. */
.bus-result {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-top: 0;
  padding: 18px 0 4px;
  background: transparent;
}

.bus-result-label {
  display: block;
  margin-bottom: 4px;
  color: rgba(var(--lagoa-brown-rgb), 0.55);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.searched {
  color: var(--lagoa-brown);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.25;
}

.bus-error {
  margin-top: 0;
  padding: 16px 0 4px;
  background: transparent;
  color: var(--lagoa-red-dark);
  font-size: 18px;
  line-height: 1.45;
  font-weight: 600;
}

/* --- Route steps --- */

/* One shared column track so the header labels line up with the rows
   busmap.js builds underneath them. */
.bus-steps-head,
.instructions .gridbus {
  display: grid;
  grid-template-columns: 72px 1fr 120px 110px;
  align-items: center;
  column-gap: 16px;
}

.bus-steps-head {
  /* Hidden until there's a route; busmap.js sets display:grid. */
  display: none;
  margin-top: 28px;
  padding: 12px 4px 14px;
  background: transparent;
  color: rgba(var(--lagoa-brown-rgb), 0.5);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-bottom: 1px solid rgba(var(--lagoa-brown-rgb), 0.16);
}

.instructions .gridbus {
  padding: 18px 4px;
  border-bottom: 1px solid rgba(var(--lagoa-brown-rgb), 0.1);
  font-size: 18px;
  color: var(--lagoa-brown);
  background: transparent;
}

.instructions .gridbus:nth-child(even) {
  background: transparent;
}

.step_number span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  background: var(--lagoa-orange);
  color: #fff;
  font-size: 18px;
  font-weight: 700;
}

/* Walking legs are the connective tissue between the bus legs that are the
   actual answer, so they're stepped back rather than given equal weight. */
.step-walk .step_number span {
  background: rgba(var(--lagoa-brown-rgb), 0.12);
  color: var(--lagoa-brown);
}

.step_instructions {
  line-height: 1.4;
}

.step_distance,
.step_eta {
  color: var(--lagoa-dark-grey);
  font-size: 16px;
  font-weight: 700;
}

/* --- Departure / arrival / line detail --- */

.businfo {
  margin-top: 8px;
}

.bustitle {
  margin: 28px 0 10px;
  padding-top: 18px;
  border-top: 1px solid rgba(var(--lagoa-brown-rgb), 0.12);
  color: rgba(var(--lagoa-brown-rgb), 0.55);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.businfo .bustitle:first-child {
  border-top: none;
  padding-top: 8px;
}

.gridbustwo {
  display: grid;
  column-gap: 16px;
  row-gap: 6px;
  padding: 12px 4px 16px;
  background: transparent;
  border-bottom: 1px solid rgba(var(--lagoa-brown-rgb), 0.08);
  font-size: 18px;
  color: var(--lagoa-brown);
}

/* Times and line numbers are short and want to align down the left edge; the
   stop and operator names take the rest. */
.grid-x2.gridbustwo {
  grid-template-columns: 110px 1fr;
}

.grid-x3.gridbustwo {
  grid-template-columns: 110px 1fr 230px;
}

.gridbustwo > div:first-child {
  color: var(--lagoa-teal);
  font-weight: 700;
}

/* ==========================================
   NOTHING IS ROUNDED
   The kiosk's shapes are square or cut on the diagonal - the mosaic, the
   sidebar rail and the listing cards all read that way. The 8px-to-1.5rem
   radii the starter project puts on every panel, card, photo, chip and
   button fight that everywhere else, and a rounded corner sitting next to a
   cut one is what made these controls look borrowed from another design.

   Radii are flattened across the board, and the cut geometry is kept for the
   things that are content in their own right - the mosaic tiles, the rail, the
   listing cards, the camera button. Navigation controls stay plain rectangles:
   language, accessibility, back, home and the list filters are chrome, and
   cutting those as well left nothing in the app reading as square, so the shapes
   stopped meaning anything.
========================================== */

/* Shutter invitation: rounded trapezoid (wider at top) via SVG mask. */
.selfie-btn {
  clip-path: none;
  background: linear-gradient(160deg, var(--lagoa-orange) 0%, var(--lagoa-orange-dark) 100%);
  -webkit-mask: url("/static/img/icones/camera-btn-shape.svg") center / 100% 100% no-repeat;
  mask: url("/static/img/icones/camera-btn-shape.svg") center / 100% 100% no-repeat;
  box-shadow: none;
  border-radius: 0 !important;
}

.selfie-btn:hover {
  background: linear-gradient(160deg, var(--lagoa-orange) 0%, var(--lagoa-orange) 100%);
  box-shadow: none;
  transform: none;
}

.selfie-btn img {
  width: 40px;
  height: 40px;
  /* Desktop camera.svg strokes are black; invert for the orange fill. */
  filter: brightness(0) invert(1);
}

body.kiosk-no-webcam .selfie-btn {
  display: none !important;
}

body.kiosk-no-touch .tts,
body.kiosk-no-touch .ico-touch,
body.kiosk-no-touch a.splash {
  display: none !important;
}

.bento-item.bento-disabled {
  pointer-events: none;
  opacity: 0.42;
  filter: grayscale(0.35);
  cursor: default;
}

/* A blanket rule over the two places page content and chrome live, rather
   than an inventory of the forty-odd selectors that set a radius between
   them: any such list goes stale the moment another one is added. Map
   subtrees are left alone - those controls are Google's own, and squaring
   them off makes the map look broken rather than deliberate. */
.content-panel *:not(.gm-style, .gm-style *),
.sidebar-content *:not(.gm-style, .gm-style *),
.main-footer * {
  border-radius: 0 !important;
}

/* Except the genuinely circular ones, which read as dots and discs rather
   than as panels with softened corners. Must stay after the rule above.
   Footer language flags are discs too. */
.content-panel .owl-dot,
.content-panel .owl-nav button,
.sidebar-content .owl-dot,
.sidebar-content .owl-nav button,
.loading-spinner,
.route-point-number,
.slider .slider-img,
.main-footer .unified-lang-btn img {
  border-radius: 50% !important;
}

/* ==========================================
   SELFIE CAMERA - full-bleed portrait

   The starter project photographed a square window inside a polaroid frame
   because its camera was mounted landscape: a 16:9 sensor lying on its side
   can only give a portrait screen a strip out of its middle, and the frame
   was there to make that crop look intended. This kiosk's camera is mounted
   turned on its side, so the whole sensor covers the 1080x1920 screen once
   it's stood upright - the photo is the screen, and there is nothing left
   for a frame to hide.
========================================== */

:root {
  /* Which way the camera is turned, and the only place it's written down:
     camera.js reads this property so the captured frame is rotated exactly
     as the preview is. If the camera ends up mounted the other way round on
     site, 90deg here fixes both. */
  --selfie-cam-rotation: -90deg;
}

/* Was a 1080x1327 box floated at 43% height - the polaroid's aspect. Now the
   stage is the screen. */
.camera-overlay .capture-area {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
}

.camera-overlay .video-stream {
  /* Laid out landscape and then stood upright, so the rotated footprint is
     exactly the portrait screen: swapping the viewport's own dimensions keeps
     that true without hardcoding 1920x1080 here.

     object-fit (inherited from style.css) still center-crops, which is what
     covers a camera that hands us 4:3 instead of 16:9. */
  width: 100vh;
  height: 100vw;
  /* style.css grows the video to the container's full height to cover the
     square window; against a landscape box that floor is taller than the
     video itself and would stretch it. */
  min-width: 0;
  min-height: 0;
  /* scaleY, not scaleX: the mirror has to be applied before the rotation, so
     it's the axis that becomes horizontal once the frame is upright. Kiosk
     selfies are mirrored so people can place themselves as in a mirror. */
  transform: translate(-50%, -50%) rotate(var(--selfie-cam-rotation)) scaleY(-1);
}

/* The sticker is dragged into place, so it has to be able to receive the touch
   that style.css turns off for it - matched on the id, since that's how style.css
   reaches it and a class alone loses. touch-action has to be none too, or the
   browser claims the gesture as a scroll and the sticker only stutters along
   behind the finger. */
.camera-overlay #frame {
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
}

.camera-overlay #frame.dragging {
  cursor: grabbing;
  /* Lifted while it's being carried, so it reads as picked up rather than as
     part of the photo underneath. */
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.45));
}

/* The controls all sit 80px higher than style.css put them: they were placed
   against a full-bleed photo with nothing along its bottom edge, and the
   municipality's frame now ends in a logo band they would otherwise cover. */
.camera-overlay .tira-foto {
  bottom: 200px;
}

.camera-overlay .escolha-stickers {
  bottom: 320px;
}

/* Clear of the chooser and of the counter chip style.css floats 30px above it,
   and faded in by camera.js only while a sticker is on the photo - see
   setDragHint(). */
.camera-overlay .sticker-hint {
  position: fixed;
  z-index: 4;
  bottom: 440px;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 10px 22px;
  border-radius: 22px;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(10px);
  color: #fff;
  font-size: 24px;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}

/* The way out of the camera. It was a translucent white disc over the preview,
   which is the one background it can't be seen against: the preview is a lit
   face, mostly bright, and frosted white on bright white is close to invisible.
   Orange because it's the only palette colour no section chrome uses, so it
   reads as "leave" rather than as part of whatever section you came from, and
   square-edged like the app's other navigation. */
.camera-overlay .back-home {
  width: 88px;
  height: 88px;
  /* Above the frame's white border, which climbs to y 1824 out at the edges. */
  bottom: 140px;
  background: linear-gradient(135deg, var(--lagoa-orange) 0%, var(--lagoa-orange-dark) 100%);
  border-radius: 0;
  border: none;
  /* Opaque now, so the blur behind it has nothing left to soften. */
  backdrop-filter: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.camera-overlay .back-home:hover {
  background: linear-gradient(135deg, var(--lagoa-orange) 0%, var(--lagoa-orange) 100%);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.4);
}

.camera-overlay .back-home img {
  width: 40px;
  height: 40px;
  /* home.svg is already white; the brightness lift style.css uses to rescue a
     dim icon on glass does nothing on a solid fill. */
  filter: none;
}
