/* ═══════════════════════════════════════════════════════════════
   ULIC · GLOW LAYER (shared enhancement stylesheet)
   Loaded AFTER each page's own <style>. No markup changes required.
   Pairs with glow.js. Brand tokens are inherited from the page.
   ═══════════════════════════════════════════════════════════════ */

/* ── 1 · Paper grain ─────────────────────────────────────────── */
body::after {
  content: "";
  position: fixed; inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0.028;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
  mix-blend-mode: multiply;
}

/* ── 2 · Reading progress + topbar depth ─────────────────────── */
.gu-progress {
  position: fixed; top: 0; left: 0; z-index: 120;
  height: 2px; width: 0;
  background: linear-gradient(90deg, var(--accent, #f0582e), var(--accent-deep, #c44215));
  box-shadow: 0 0 8px rgba(240, 88, 46, 0.45);
  transition: width 80ms linear;
  pointer-events: none;
}
.topbar { transition: box-shadow 240ms ease; }
.topbar.gu-scrolled { box-shadow: 0 10px 30px -18px rgba(4, 67, 152, 0.28), 0 1px 0 rgba(4, 67, 152, 0.05); }

/* ── 3 · Scroll reveal (JS gates via html.gu-ready) ──────────── */
html.gu-ready [data-gu] {
  transition:
    opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--gu-d, 0ms);
  will-change: opacity, transform;
}
html.gu-ready [data-gu]:not(.gu-in) {
  opacity: 0;
  transform: translateY(16px);
}
html.gu-ready [data-gu].gu-in {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  html.gu-ready [data-gu] { transition: none; }
  html.gu-ready [data-gu]:not(.gu-in) { opacity: 1; transform: none; }
}

/* ── 4 · Editorial ghost numerals on section heads ───────────── */
.section-head { position: relative; }
.section-head[data-gu-num]::after {
  content: attr(data-gu-num);
  position: absolute;
  right: -6px;
  bottom: -4px;
  font-family: var(--f-display, Georgia, serif);
  font-weight: 600;
  font-style: italic;
  font-size: clamp(88px, 11vw, 168px);
  line-height: 0.7;
  letter-spacing: -0.03em;
  color: var(--ink, #044398);
  opacity: 0.05;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
.section-head > * { position: relative; z-index: 1; }

/* ── 5 · Hover language ──────────────────────────────────────── */
.cta {
  transition: background 150ms, color 150ms, border-color 150ms,
              transform 180ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 180ms;
}
.cta:hover { transform: translateY(-2px); }
.cta:active { transform: translateY(0); transition-duration: 60ms; }
.cta-primary:hover { box-shadow: 0 14px 30px -12px rgba(240, 88, 46, 0.55); }
.cta-ghost:hover, .cta-on-dark:hover { box-shadow: 0 14px 30px -14px rgba(4, 67, 152, 0.4); }
.cta-ghost-on-dark:hover { box-shadow: 0 14px 30px -14px rgba(0, 0, 0, 0.35); }

/* underline sweep for quiet links */
.footer ul a, .doc a.inline, .faq-a .inner a, .note-card a, .body-cols a, .prose-block a {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 1px;
  background-position: 0 96%;
  transition: background-size 220ms cubic-bezier(0.22, 1, 0.36, 1), color 150ms;
}
.footer ul a:hover, .doc a.inline:hover, .faq-a .inner a:hover, .note-card a:hover,
.body-cols a:hover, .prose-block a:hover { background-size: 100% 1px; }

/* cards that are links get lift */
a.prep-card, a.fmt-link, .exam-links a, a.chip, .formats-row .fmt-link {
  transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1),
              box-shadow 200ms, border-color 150ms, background 150ms, color 150ms;
}
a.prep-card:hover { transform: translateY(-3px); box-shadow: 0 24px 48px -24px rgba(4, 67, 152, 0.4); }
.exam-links a:hover, a.chip:hover { transform: translateY(-2px); }

/* informational cells: quiet tint on hover, orange numeral wakes up */
.kv, .pillar, .stat { transition: background 180ms; }
.kv:hover, .pillar:hover { background: var(--paper-2, #f7f9fc); }
.pillar:hover .num, .kv:hover .k { color: var(--accent-deep, #c44215); }
.checklist li { transition: background 150ms, padding-left 200ms cubic-bezier(0.22, 1, 0.36, 1); }
.checklist li:hover { padding-left: 10px; }

/* tables: settle + tabular figures */
.data-table tbody tr { transition: background 140ms; }
.data-table td.num-col, .stat .v, .kv .v { font-variant-numeric: tabular-nums; }

/* FAQ affordance.
   An open item used to also take a white background, which read as an
   arbitrary block appearing behind whichever question you tapped. The open
   state is already carried by the question turning accent-deep and the +
   becoming a -, so the background change was redundant as well as ugly
   (Ben, 29 Jul 2026). The list now stays one continuous surface. */
.faq-q { transition: padding-left 200ms cubic-bezier(0.22, 1, 0.36, 1); }
.faq-item:hover .faq-q { padding-left: 10px; }
/* Only 8 of the 34 pages using this accordion declared the orange open state;
   the other 26 relied on the white background alone, so removing it would have
   left them with just the +/- icon. Declaring it here gives every page the same
   marker: the open question turns accent-deep, the icon becomes a minus. */
.faq-item.open .faq-q { color: var(--accent-deep); }

/* ── 6 · Dark bands: top hairline glow ───────────────────────── */
.stats, .final, .honest, .corporate {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
              inset 0 80px 120px -80px rgba(26, 91, 178, 0.55);
}

/* ── 7 · Quality floor ───────────────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--accent, #f0582e);
  outline-offset: 3px;
}
[id] { scroll-margin-top: 96px; }
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ── 8 · Site-wide alignment pass (18 Jul 2026) ──────────────────
   This sheet loads after each page's inline <style>, so the rules
   below override the per-page values on every page at once. */

/* Tagline band: tagline dead-centre, sides symmetric (desktop grid only;
   the ≤980px inline media query still stacks it to one column) */
@media (min-width: 981px) {
  .tagline-stamp { grid-template-columns: 1fr auto 1fr; }
}

/* Exam-card corner tags: never wrap into a ragged block; one line,
   pinned to the same right-aligned position in every card */
.fam-head { align-items: baseline; }
.fam-head .use { max-width: none; white-space: nowrap; }

/* Final-CTA / hero lead form: keep the office-hours note on one centred line
   under the "Infórmate gratis" button */
.lead-form .lf-note {
  white-space: nowrap; text-align: center; max-width: none;
  font-size: clamp(8px, 2.5vw, 9.5px); letter-spacing: 0.03em;
}

/* Footer v2: strict grid, one exam per link, uniform type */
@media (min-width: 981px) {
  .footer-grid { grid-template-columns: 1.15fr 1.6fr 0.8fr 1fr; gap: 44px; }
}
.footer ul li { font-size: 14.5px; color: var(--ink-soft); }
.footer .foot-cols-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 26px; align-items: start; }
.footer .brand-block img { height: 44px; }
.footer .brand-block .cred-line {
  margin: 16px 0 0; font-family: var(--f-mono); font-size: 10.5px;
  letter-spacing: var(--letter-mono, 0.14em); text-transform: uppercase;
  color: var(--ink-mute, #6781a4); font-weight: 600; line-height: 2;
}
.footer .footer-source { text-align: center; }
.footer-bottom { flex-direction: column; align-items: center; text-align: center; gap: 9px; }


/* --- Click-to-load Google Maps | RGPD: no google request until the visitor opts in (add_tracking.py) --- */
.cky-map{position:absolute;inset:0;width:100%;height:100%;display:flex;flex-direction:column;gap:9px;align-items:center;justify-content:center;text-align:center;padding:10px;background:#e7edf6;background-image:repeating-linear-gradient(0deg,transparent,transparent 13px,rgba(4,67,152,.05) 13px,rgba(4,67,152,.05) 14px),repeating-linear-gradient(90deg,transparent,transparent 13px,rgba(4,67,152,.05) 13px,rgba(4,67,152,.05) 14px);}
.cky-map-btn{font-family:var(--f-mono,ui-monospace,monospace);font-size:12px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;color:#fff;background:var(--ulic-blue,#044398);border:0;padding:11px 18px;cursor:pointer;transition:background .15s ease,transform .15s ease;}
.cky-map-btn:hover{background:var(--cambridge-orange,#f0582e);transform:translateY(-1px);}
.cky-map-note{font-family:var(--f-mono,ui-monospace,monospace);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;color:#5b769c;}
.cky-map.is-loaded{background:none;padding:0;}
.footer .map-mini .cky-map{gap:0;}
.footer .map-mini .cky-map-note{display:none;}

/* Hero review card: sits in the under-hero band on pages without a walk-through
   video (replaces the .hero-video card). Matches the .hero-video frame styling. */
.hero-review{background:#fff;border:1px solid var(--rule-strong);border-top:4px solid var(--ink);align-self:start;display:flex;flex-direction:column;padding:clamp(24px,2.6vw,34px);}
.hero-review .hr-rating{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 18px;}
.hero-review .hr-stars{color:var(--accent);font-size:16px;letter-spacing:3px;line-height:1;}
.hero-review .hr-score{font-family:var(--f-display);color:var(--ink);font-size:19px;font-weight:600;}
.hero-review .hr-count{font-family:var(--f-mono);font-size:10px;letter-spacing:var(--letter-mono);text-transform:uppercase;color:var(--ink-mute);font-weight:500;}
.hero-review .hr-q{font-family:var(--f-display);color:var(--ink);font-size:clamp(17px,1.9vw,21px);font-weight:500;line-height:1.34;letter-spacing:var(--letter-display);margin:0 0 18px;}
.hero-review .hr-attr{font-family:var(--f-mono);font-size:10.5px;letter-spacing:var(--letter-mono);text-transform:uppercase;color:var(--ink-soft);font-weight:500;line-height:1.5;}
.hero-review .hr-attr b{color:var(--ink);font-weight:600;}

/* Under-hero media band layout: the lead heading + review card share the top row,
   the body text spans the full width below. With no review card the lead goes
   full-width too. Applied by adding .hr-media to .media-row / .argumento-media. */
.hr-media.hr-media{grid-template-columns:1.05fr 0.95fr;column-gap:clamp(28px,4vw,52px);row-gap:clamp(18px,2.4vw,32px);align-items:start;grid-template-areas:"lead card" "body body";}
.hr-media .hr-lead{grid-area:lead;align-self:center;}
.hr-media .hr-lead .manifesto-lead{margin-bottom:0;}
.hr-media .hero-review{grid-area:card;align-self:center;}
.hr-media .hr-body{grid-area:body;}
/* Exam pages: body paragraphs run the FULL width under the lead + review card
   (Ben 28 Jul: match the Curso Trimestral proportion — full-measure lines, never
   narrower). Scoped to .media-text; the .am-text argumento bodies already do this. */
.hr-media .media-text.hr-body .mt-p{max-width:none;}
.hr-media.hr-media:not(:has(.hero-review)){grid-template-columns:1fr;grid-template-areas:"lead" "body";}

/* ── Course colour is fixed per format, on EVERY page (Ben 28 Jul): Curso trimestral
   = paper/white, Intensivo = blue, One to One = ORANGE. The .fmt-acc accordion
   already does this with its theme- classes; the single-format .prep-card block used
   on the pages with only one option had no orange variant and rendered One to One in
   blue. `.is-oto` supplies it. Everything inside .prep-card.featured that was tinted
   with --accent (tag, em, list bullets, CTA) has to flip to white here, or it would
   be orange-on-orange and vanish. */
.prep-card.featured.is-oto { background: var(--accent); }
.prep-card.featured.is-oto .p-tag { color: rgba(255,255,255,0.9); }
.prep-card.featured.is-oto h3 em,
.prep-card.featured.is-oto .p-meta .v em { color: #fff; }
.prep-card.featured.is-oto .p-list li::before { color: #fff; }

/* Single-format pages must all look like the one-panel accordion (Ben 28 Jul,
   Aptis Advanced is the reference): the meta becomes ONE flex row with a single
   top rule instead of a 2x2 grid boxed by two rules, and the CTA becomes the
   same solid white button (.cta-on-dark) instead of a mono text link. The four
   `.p-list` bullets stay — they are real copy these pages carry and Aptis does
   not, and they are the only reason the block is still slightly taller. */
.prep-card .p-meta {
  display: flex; flex-wrap: wrap; gap: 0;
  padding: 4px 0 0; margin-bottom: 26px;
  border-bottom: 0;
}
.prep-card .p-meta > div { padding: 16px 30px 0 0; }
/* Same solid white button the accordion uses (.cta.cta-on-dark). The colour is set
   at .prep-card.featured.is-oto specificity (0,4,0) so it can't be beaten by the
   orange-card overrides above — that mismatch previously left blue text on a blue
   hover background, i.e. an empty-looking button. */
.prep-card .p-cta,
.prep-card.featured .p-cta,
.prep-card.featured.is-oto .p-cta {
  margin-top: auto; align-self: flex-start;
  display: inline-flex; align-items: center; gap: 10px;
  border: 1px solid #fff; padding: 13px 22px;
  font-family: var(--f-body); font-weight: 600; font-size: 14px;
  letter-spacing: 0.02em; text-transform: none;
  background: #fff; color: var(--ink);
  transition: background 150ms, color 150ms, border-color 150ms;
}
.prep-card:hover .p-cta,
.prep-card.featured:hover .p-cta,
.prep-card.featured.is-oto:hover .p-cta {
  background: var(--accent-deep); border-color: var(--accent-deep); color: #fff;
}

/* ── .note-card beside body copy is an ASIDE, never a stretched panel (Ben 28 Jul).
   Two faults produced the empty box in §05 Validez / §06 En España:
   1. the card stretched to the prose height, so a 3-line note drew a 460px box;
   2. .two-col.uneven gave the WIDER column (1.15fr) to the SHORT note and the
      narrower one to the long prose, which maximised the mismatch.
   Fix: the card hugs its content at the top, and the prose takes the wider column.
   The ratio flip is scoped with :has() so Contacto's .two-col.uneven (loc-block,
   two equally tall columns) keeps its original proportions, and it is held inside
   min-width so the 980px single-column collapse still wins on mobile. */
.two-col > .note-card { align-self: center; }
@media (min-width: 981px) {
  .two-col.uneven:has(> .note-card) { grid-template-columns: 1.15fr 0.85fr; }
}

/* ── Mobile horizontal overflow (Ben 28 Jul; desktop was never affected).
   Root cause in every case: a grid item defaults to min-width:auto, i.e. it
   refuses to shrink below its min-content, so one wide table or card forces its
   track past the viewport and the whole page scrolls sideways. */

/* 1. Let the columns shrink. The tables already sit in .table-wrap
      (overflow-x:auto), so they scroll inside the column as designed instead of
      dragging the page with them. Fixes C1 + C2 (§03 Formato) and Home (.fam). */
.two-col > *, .fam-grid > .fam { min-width: 0; }

/* 2. .t-grid (3 testimonial cards) had no mobile collapse at all, so three
      columns stayed side by side on a phone. Match the cadence the rest of the
      site uses for 3-up grids (.pillars et al): 2-up at 980, 1-up at 580.
      Fixes One to One + Curso Intensivo Pre-Examen + Verano. */
@media (max-width: 980px) { .t-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 580px) { .t-grid { grid-template-columns: 1fr; } }

/* ── .fmt-acc horizontal accordion: the CTA button at the bottom of a panel was
   clipped (Ben 28 Jul, empresas). .fmt-panel is overflow:hidden and BOTH its
   children (.fmt-collapsed and .fmt-content) are position:absolute, so nothing
   inside can push the panel taller than .fmt-acc's fixed
   height:clamp(420px,46vw,500px) — and that height SHRINKS as the viewport
   narrows while the copy GROWS from wrapping. Measured overflow on empresas:
   69-189px at every width from 800 to 1700, so the button was never fully
   visible on desktop; it only appeared below 760px where the accordion stacks.
   Fix: the active panel's content goes back in flow so it sets the height, and
   the old fixed height becomes a min-height floor. Self-sizing, so pages whose
   copy already fits (Home, B2 First) keep their 500px and only the ones that
   need more (TOEFL +39, empresas +189) grow. Scoped above the 760px breakpoint
   so the stacked mobile layout is untouched.
   28 Jul, second pass: putting only the ACTIVE panel's content in flow made the
   accordion's height jump every time you changed panel (it grew to the longest
   panel's copy, then snapped back) — the "expands down then comes back up"
   Ben reported. EVERY panel's content goes in flow instead, so the container
   settles on the tallest of the three once and never moves; the collapsed ones
   are still invisible (opacity 0) and clipped by overflow:hidden. */
@media (min-width: 761px) {
  .fmt-acc { height: auto; min-height: clamp(420px, 46vw, 500px); }
  .fmt-panel .fmt-content { position: relative; inset: auto; }
}

/* 3. The legal pages write bare <table> and never defined .table-wrap, so the
      4-column cookie table ran off the screen. Same scroll-in-a-wrapper pattern
      the exam pages use (making the table itself display:block or table-layout:
      fixed was tried and both mangle a 4-column table at 375px). */
.doc .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.doc .table-wrap table { margin: 0; }
/* Without this the table collapses to its 540px min-content on a phone, the
   long "Finalidad" column wraps to a dozen lines and every row balloons (763px
   tall vs 380px). 700px keeps the desktop column proportions and scrolls. */
@media (max-width: 760px) { .doc .table-wrap table { min-width: 700px; } }
.doc .table-scroll-hint { display: none; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: var(--letter-mono); text-transform: uppercase; color: var(--ink-mute); margin-top: 12px; font-weight: 500; }
@media (max-width: 760px) { .doc .table-scroll-hint { display: block; } }
@media (max-width:980px){.hr-media.hr-media{grid-template-columns:1fr;grid-template-areas:"lead" "card" "body";}.hr-media.hr-media:not(:has(.hero-review)){grid-template-areas:"lead" "body";}}

/* ── Mobile snag fixes · 29 Jul 2026 ────────────────────────────────────────
   1. Lead-form card sat 18px off-centre on phones. The card's min-content is
      the nowrap .lf-note (289px) plus 64px of padding = 353px, wider than the
      335px grid. A `1fr` track is minmax(auto,1fr), so it stretched to fit and
      pushed the card right. Letting the note wrap removes the cause; min-width:0
      stops any future wide child doing the same.
   2. CookieYes's revisit badge is fixed bottom-left at z-index 999999, directly
      on top of the WhatsApp float. Stacked above it instead, left edges aligned.
      !important is required: CookieYes sets its position from its own stylesheet. */
@media (max-width: 900px) {
  .hero-grid > *,
  .final-grid > * { min-width: 0; }
  .lead-form .lf-note { white-space: normal; }
}
.cky-btn-revisit-wrapper {
  bottom: 88px !important;   /* WhatsApp float is 72-76px tall from the bottom */
  left: 20px !important;     /* matches .wa-float so the two align vertically */
}

/* Footer on phones: Formatos and Contacto side by side rather than stacked.
   Each page's own <style> collapses .footer-grid to a single column below
   580px, which left Formatos using the left half and the right half empty,
   pushing Contacto down a full screen. Brand and Exámenes still span the full
   width - Exámenes has its own two-column list and needs the room. This rule
   lives in glow.css, which loads after that block, so it wins on the cascade. */
@media (max-width: 580px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-grid > .brand-block,
  .footer-grid > div:nth-child(2) { grid-column: 1 / -1; }
}

/* Hero band bleeding into the section below it (exam + testimonios pages).
   .hero-bg is inset top:-8% height:116%, so the wave gradient deliberately
   overshoots the hero by 8% at each end. .hero has overflow:visible, so on
   phones - where the hero stacks to ~2200px and 8% is ~178px - the band ran
   past the hero and cut straight through the first section's heading.
   Home, course and empresas pages never showed it only because the section
   after their hero (.stats / .corporate) is opaque and covers the spill.
   Giving the following .section the same opaque treatment fixes it the same
   way, and leaves the hero waves completely untouched. */
.hero + .section {
  position: relative;
  z-index: 1;
  background: var(--paper);
}

/* Double rule under the §01 heading: .section-head has a border-bottom and
   .fc-viewport its own border-top, landing two blue lines 56px apart. The
   carousel is a self-contained strip that needs both of its own rules, so the
   head's underline is the redundant one - dropped only where the carousel
   directly follows, leaving every other section head unchanged. */
.section-head:has(+ .ficha-carousel) { border-bottom: 0; }

/* Floats step aside for the footer. glow.js adds .gu-footer-in to <html> while
   the footer is on screen; without this the WhatsApp float, cookie badge and
   summer FAB sit on top of the footer's contact links and block the taps.
   pointer-events:none matters as much as the fade - a fully transparent button
   still swallows clicks. */
.gu-footer-in .wa-float,
.gu-footer-in .summer-corner,
.gu-footer-in .cky-btn-revisit-wrapper {
  opacity: 0;
  pointer-events: none;
  transform: translateY(12px);
}
.wa-float,
.summer-corner,
.cky-btn-revisit-wrapper {
  transition: opacity .25s ease, transform .25s ease;
}
/* The floats step aside for the FIRST SCREEN, phones only (29 Jul 2026).
   Below 980px the hero is one full-width column, so its content runs straight
   under the fixed floats: the CookieYes badge sat mid-sentence on the C2 lede,
   the WhatsApp float covered empresas' "Ver programas", and on Contacto the
   two together covered the WhatsApp number. There is nowhere to move a
   bottom-corner float TO on a 393px screen, so they wait instead - glow.js
   adds .gu-hero-in below 200px of scroll and they fade in past that. The class
   name is historical: it keyed off the hero element before Contacto and
   Testimonios turned out not to have the anchor it used. The first screen
   already carries the hero CTAs, the lead form and the topbar icon button, so
   nothing is lost by the wait.
   Desktop is untouched: there the floats sit in empty page margin.
   The summer FAB is deliberately NOT in this list (Ben, 29 Jul): it is a
   seasonal campaign entry point and stays visible from the first screen. It
   still steps aside for the footer below - that rule stops it covering the
   footer's contact links, which is a different problem. */
@media (max-width: 980px) {
  .gu-hero-in .wa-float,
  .gu-hero-in .cky-btn-revisit-wrapper {
    opacity: 0;
    pointer-events: none;
    transform: translateY(12px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .wa-float,
  .summer-corner,
  .cky-btn-revisit-wrapper { transition: none; }
  .gu-footer-in .wa-float,
  .gu-footer-in .summer-corner,
  .gu-footer-in .cky-btn-revisit-wrapper,
  .gu-hero-in .wa-float,
  .gu-hero-in .cky-btn-revisit-wrapper { transform: none; }
}

/* ── Three mobile snags · 29 Jul 2026 ──────────────────────────────────────
   1. Final-CTA buttons 210px tall on phones. `.final-links .fmt-link` sets
      `flex: 1 1 210px`, which is a WIDTH on desktop where the container is a
      row - four cards across. The mobile query flips the container to
      `flex-direction: column`, so that same basis becomes a HEIGHT and every
      button inflated to 210px for 104px of content. Resetting the basis lets
      them size to their content.
   2. Exam kickers on the homepage. `.fam-head` is a nowrap flex row with
      `space-between`, so the orange `.use` started at a different left edge on
      every card (136 / 166 / 139 / 195px) and IELTS and Linguaskill pushed past
      the card edge entirely. Stacking it under the name aligns all of them and
      removes the overflow.
   3. Footer map trapped in the 158px Contacto column. It reads better closing
      off the footer full width, so it breaks out across both columns - the
      calc mirrors the 1fr 1fr grid and its 20px gap. */
@media (max-width: 640px) {
  .final-links .fmt-link { flex: 0 0 auto; max-width: none; }
  .fam .fam-head { flex-direction: column; align-items: flex-start; gap: 4px; }

  /* 4. Cambridge exam rows on the homepage (B2 / C1 / C2 in the dark card).
     Same disease as the fam kickers: `.lv` is right-aligned with max-width
     22ch, so on a phone it wraps into a ragged 2-3 line block jammed against
     the arrow ("C2 · DOMINIO PROFESIONAL" ran to three lines under the C2
     name, Ben 29 Jul). The row regrids to name + arrow on the first line and
     the kicker under the name, left-aligned, one line each. */
  .fam-flagship .exam-links a { grid-template-columns: 1fr auto; gap: 4px 16px; }
  .fam-flagship .exam-links .nm { grid-row: 1; grid-column: 1; }
  .fam-flagship .exam-links .go { grid-row: 1; grid-column: 2; align-self: center; }
  .fam-flagship .exam-links .lv {
    grid-row: 2; grid-column: 1 / -1;
    justify-self: start; text-align: left; max-width: none;
  }

  /* 5. Price row on the course pages (Trimestral, One to One). Same disease
     again: `.per` is a long right-aligned two-line mono note sharing a flex
     row with the price, and on a phone it squeezed the price until the euro
     sign wrapped onto its own line ("295" / "€"). Stack them instead. */
  .program .p-price { flex-direction: column; align-items: flex-start; gap: 6px; }
  .program .p-price .amount { white-space: nowrap; }
  .program .p-price .per { text-align: left; }
}
@media (max-width: 580px) {
  .footer .map-mini {
    width: calc(200% + 20px);
    margin-left: calc(-100% - 20px);
  }
}

/* FAQ rows turned white and stayed white on phones. The only rule painting them
   is `.faq-item summary:hover`, a desktop affordance - but iOS keeps :hover on
   the last-tapped element until you tap elsewhere, so the white square followed
   the finger rather than the open question, appearing on closed rows as often
   as open ones. Restricted to devices that genuinely hover; touch gets nothing,
   which is correct since the +/x already signals open state. */
@media (hover: none), (pointer: coarse) {
  .faq-item summary:hover { background: transparent; }
}

/* Utility bar: match the homepage everywhere. The homepage and empresas hide
   the right-hand links and the address below 980px, leaving one tidy line of
   credential. The four course pages (x2 languages) never got that rule, so on
   phones their bar kept WhatsApp + email on the right, squeezed the credential
   into two lines and grew the bar from 38px to 59px - the lopsided strip in the
   report. Nothing is lost by hiding them: the WhatsApp float is always present
   and the footer carries the email. */
@media (max-width: 980px) {
  .utility .left a:last-child,
  .utility .right { display: none; }
}

/* ── Sticky-hover sweep · 29 Jul 2026 ──────────────────────────────────────
   iOS applies :hover to the last-tapped element and holds it until the next
   tap, so any hover style on something that STAYS on screen freezes there.
   That is what made the FAQ rows look randomly white.
   Audited all 41 hover rules. Most sit on links that navigate away, where the
   page unloads before a stuck state is visible - those are left alone. The
   rules below are the ones on elements that survive the tap: same-page anchors,
   toggles, cards and the floats. Values restate each element's real base, taken
   from computed styles rather than guessed. */
@media (hover: none), (pointer: coarse) {

  /* Lift + shadow. Worst offenders: a card left floating after a tap. */
  .cta:hover,
  a.prep-card:hover,
  .exam-links a:hover,
  a.chip:hover,
  .sub-card:hover,
  .watch-interview:hover,
  .wa-float:hover,
  .cky-map-btn:hover,
  .lang-btn:hover { transform: none; }

  .cta-primary:hover,
  .cta-ghost:hover,
  .cta-on-dark:hover,
  .cta-ghost-on-dark:hover,
  a.prep-card:hover,
  .sub-card:hover { box-shadow: none; }

  .link-arrow:hover svg,
  .program .p-cta:hover svg,
  .fam-flagship .exam-links a:hover .go svg { transform: none; }

  /* Indent-on-hover: a row that stays nudged in reads as misaligned. */
  .faq-item:hover .faq-q { padding-left: 4px; }
  .checklist li:hover,
  .fam-flagship .exam-links a:hover { padding-left: 0; }

  /* Colour and background inversions on elements that stay put. */
  .fam-reveal:hover { background: #fff; color: var(--ink); }
  .kv:hover, .pillar:hover { background: transparent; }
  .kv:hover .k { color: var(--ink-mute, #6781a4); }
  .footer .map-mini:hover iframe { filter: grayscale(1); opacity: .9; }
  .lang-btn:hover { opacity: 1; filter: none; }
}

/* ── FAQ: one style everywhere · 29 Jul 2026 ───────────────────────────────
   The site had grown three different accordions: the two homepages use
   <details> with a plain orange + that rotates into an x; 26 exam pages use a
   bordered box that fills solid orange on open; 8 course pages use a bordered
   box with orange bars. Ben picked the homepage look, with the open question in
   orange (Ben, 29 Jul 2026).
   Both boxed variants are restyled here rather than in 34 files: the border and
   fill come off, the bars go accent-orange, and on open the whole glyph rotates
   45deg so the + becomes an x - matching the homepage instead of collapsing to
   a minus. Their own open-state rules (solid fill, white bars, the hidden
   vertical bar) are cancelled below. */
.faq-q .icon,
.faq-q .ico {
  border: 0;
  background: none;
  width: 26px; height: 26px;
}
.faq-q .icon::before, .faq-q .icon::after,
.faq-q .ico::before,  .faq-q .ico::after { background: var(--accent); }

/* Cancel the old open-state treatments so both bars survive the rotation. */
.faq-item.open .faq-q .icon { background: none; border-color: transparent; }
.faq-item.open .faq-q .icon::before,
.faq-item.open .faq-q .icon::after { background: var(--accent); transform: none; }
.faq-item.open .faq-q .ico::before {
  opacity: 1;
  transform: translateX(-50%);      /* .ico centres its bars with a translate */
}

/* The glyph itself turns, + into x, as on the homepage. */
.faq-q .icon, .faq-q .ico { transition: transform 200ms; }
.faq-item.open .faq-q .icon,
.faq-item.open .faq-q .ico { transform: rotate(45deg); }

/* Open question in orange on every variant, homepages included.
   The 26 exam pages wrap the text in <span class="q">, which sets its own
   color: var(--ink) and so ignored the colour on the button - the reason
   Linguaskill and Aptis Advanced stayed navy while the course pages, whose
   span carries no colour, went orange. Making .q inherit fixes those 26. */
.faq-item.open .faq-q,
.faq-item[open] .q-txt { color: var(--accent-deep); }
.faq-item.open .faq-q .q { color: inherit; }

/* ═══════════════════════════════════════════════════════════════
   30 · TOPBAR CTA → ICON BUTTON ON PHONES (29 Jul 2026)
   ═══════════════════════════════════════════════════════════════
   Each page carries exactly one topbar CTA: .cta-ghost = level test on all
   48 pages, .cta-primary = "Solicitar propuesta" on empresas (ES + EN).
   On phones the ghost one was hidden outright (the per-page inline rule
   `@media (max-width:440px) { .topbar-inner .cta-ghost { display:none } }`),
   so the single most important CTA on the site vanished on mobile, while
   empresas' primary one survived at full width and wrapped onto two lines,
   swallowing the header (Ben, 29 Jul).

   Both collapse to a 42px icon square instead - the summer FAB's treatment,
   squared to match the topbar's other chrome (.sm-x, .sp-x). Icons ride in
   as mask-image data URIs so they inherit currentColor and invert on hover
   with no extra markup: the label text stays in the DOM, visually hidden,
   so the button keeps its accessible name across all 50 pages without
   touching a single one.

   Specificity is deliberate: glow.css loads BEFORE the two inline <style>
   blocks at the end of each head, so 0,3,0 is needed to beat the 0,2,0
   hide rule sitting in all 50 of them. */
@media (max-width: 580px) {
  .topbar .topbar-right .cta {
    display: grid; place-items: center; position: relative;
    width: 42px; height: 42px; padding: 0; gap: 0; flex: 0 0 auto;
  }
  /* the ghost CTA's inline arrow - the icon replaces it */
  .topbar .topbar-right .cta > svg { display: none; }

  /* label kept for screen readers, not display:none (that would drop the
     accessible name and leave an unlabelled button) */
  .topbar .topbar-right .cta > span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }

  .topbar .topbar-right .cta::before {
    content: ""; width: 21px; height: 21px; background: currentColor;
    -webkit-mask: var(--tb-ico) center / contain no-repeat;
            mask: var(--tb-ico) center / contain no-repeat;
  }
  /* level test: document + check */
  .topbar .topbar-right .cta-ghost {
    --tb-ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='square'%3E%3Cpath d='M5 3h9l5 5v13H5z'/%3E%3Cpath d='M14 3v5h5'/%3E%3Cpath d='M8.5 14.2l2.4 2.4 4.6-4.6'/%3E%3C/svg%3E");
  }
  /* solicitar propuesta: envelope */
  .topbar .topbar-right .cta-primary {
    --tb-ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='square'%3E%3Cpath d='M3 6h18v12H3z'/%3E%3Cpath d='M3.6 6.6l8.4 6.6 8.4-6.6'/%3E%3C/svg%3E");
  }
}
