Last modified by Veronika Kocher on Friday, 04 Sep 2026 14:45

From version 5.11
edited by Veronika Kocher
on Thursday, 03 Sep 2026 13:04
Change comment: There is no comment for this version
To version 5.10
edited by Veronika Kocher
on Thursday, 03 Sep 2026 10:09
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -261,13 +261,12 @@
261 261  </div>
262 262  {{/html}}
263 263  
264 -{{html clean="false"}}
265 -<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_START -->
264 +\n\n
265 +\n\n{{html clean="false"}}
266 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_START -->
266 266  
267 -<style data-sn-service-navigator-master="1">
268 -/* =========================================================
269 - MASTER TYPOGRAPHY
270 - ========================================================= */
268 +<style data-sn-horizontal-box-harmonizer="7">
269 +/* ---------- Typography: never justify headings ---------- */
271 271  #xwikicontent h1,
272 272  #xwikicontent h2,
273 273  #xwikicontent h3,
... ... @@ -289,73 +289,88 @@
289 289   word-spacing:normal!important;
290 290  }
291 291  
292 -/* =========================================================
293 - COMING SOON / IN VORBEREITUNG
294 - ========================================================= */
295 -.sn-coming-soon-badge{
296 - display:inline-block;
297 - vertical-align:middle;
298 - margin-left:10px;
299 - padding:4px 8px;
300 - border-radius:14px;
301 - border:1px solid #efd9cc;
302 - background:#fff4ee;
303 - color:#8a4d36;
304 - font-size:11px;
305 - font-weight:700;
306 - letter-spacing:.055em;
307 - line-height:1.2;
308 - white-space:nowrap;
309 -}
310 -
311 -/* =========================================================
312 - NATIVE / DETAIL CALLOUT GEOMETRY
313 - Preserve existing backgrounds.
314 - ========================================================= */
315 -#xwikicontent .arsn-info,
316 -#xwikicontent .dmp-info,
317 -#xwikicontent .ido-info,
318 -#xwikicontent .pp-info,
319 -#xwikicontent .infomessage{
291 +/* ---------- Native XWiki semantic callouts ---------- */
292 +#xwikicontent .box.infomessage,
293 +#xwikicontent .infomessage,
294 +#xwikicontent .box.warningmessage,
295 +#xwikicontent .warningmessage,
296 +#xwikicontent .box.successmessage,
297 +#xwikicontent .successmessage,
298 +#xwikicontent .box.errormessage,
299 +#xwikicontent .errormessage,
300 +#xwikicontent [data-sn-callout-harmonized="1"]{
320 320   box-sizing:border-box!important;
321 - border-left:4px solid #21b6d7!important;
322 - border-radius:8px;
302 + border-radius:8px!important;
303 + border-top:1px solid rgba(0,0,0,.08)!important;
304 + border-right:1px solid rgba(0,0,0,.08)!important;
305 + border-bottom:1px solid rgba(0,0,0,.08)!important;
306 + border-left:4px solid var(--sn-callout-accent,#21b6d7)!important;
307 + padding:14px 16px!important;
323 323   text-align:left!important;
309 + text-justify:auto!important;
324 324   word-spacing:normal!important;
311 + box-shadow:none!important;
325 325  }
326 326  
327 -#xwikicontent .arsn-warn,
328 -#xwikicontent .dmp-warn,
329 -#xwikicontent .ido-warn,
330 -#xwikicontent .pp-warn,
331 -#xwikicontent .warn,
332 -#xwikicontent .warning,
333 -#xwikicontent .warningmessage,
314 +#xwikicontent .box.infomessage,
315 +#xwikicontent .infomessage{
316 + --sn-callout-accent:#21b6d7;
317 + background:#eaf7fb!important;
318 +}
319 +
320 +#xwikicontent .box.warningmessage,
321 +#xwikicontent .warningmessage{
322 + --sn-callout-accent:#f0aa00;
323 + background:#fff8e7!important;
324 +}
325 +
326 +#xwikicontent .box.successmessage,
327 +#xwikicontent .successmessage{
328 + --sn-callout-accent:#38b76a;
329 + background:#edf9f1!important;
330 +}
331 +
332 +#xwikicontent .box.errormessage,
334 334  #xwikicontent .errormessage{
335 - box-sizing:border-box!important;
336 - border-left:4px solid #e94b13!important;
337 - border-radius:8px;
338 - text-align:left!important;
339 - word-spacing:normal!important;
334 + --sn-callout-accent:#e94b13;
335 + background:#fff1ec!important;
340 340  }
341 341  
342 -[data-sn-master-equal-height],
343 -[data-sn-master-first-row]{
338 +#xwikicontent [data-sn-callout-harmonized="1"] > :first-child,
339 +#xwikicontent .infomessage > :first-child,
340 +#xwikicontent .warningmessage > :first-child,
341 +#xwikicontent .successmessage > :first-child,
342 +#xwikicontent .errormessage > :first-child{
343 + margin-top:0!important;
344 +}
345 +
346 +#xwikicontent [data-sn-callout-harmonized="1"] > :last-child,
347 +#xwikicontent .infomessage > :last-child,
348 +#xwikicontent .warningmessage > :last-child,
349 +#xwikicontent .successmessage > :last-child,
350 +#xwikicontent .errormessage > :last-child{
351 + margin-bottom:0!important;
352 +}
353 +
354 +#xwikicontent [data-sn-equal-height-managed],
355 +#xwikicontent [data-sn-first-section-aligned],
356 +#xwikicontent [data-sn-first-content-aligned]{
344 344   box-sizing:border-box;
345 345  }
346 346  </style>
347 347  
348 -<script data-sn-service-navigator-master="1">
361 +<script data-sn-horizontal-box-harmonizer="7">
349 349  (function(){
350 350   "use strict";
351 351  
352 - var PAGE_KIND="detail";
353 - var PLACEHOLDER=false;
354 - var PLACEHOLDER_LABEL="COMING SOON";
355 -
356 356   var MOBILE_MAX=767;
357 357   var ROW_TOLERANCE=6;
367 + var MIN_W=150;
368 + var MIN_H=36;
369 + var MAX_PARENT_CHILDREN=18;
370 + var SEARCH_DEPTH=5;
358 358   var RESIZE_DEBOUNCE=220;
372 + var MIN_WIDTH_CHANGE=2;
359 359  
360 360   var root=
361 361   document.getElementById("xwikicontent")||
... ... @@ -365,114 +365,142 @@
365 365  
366 366   if(!root)return;
367 367  
368 - var managedHeight=new Set();
369 - var managedMargin=new Set();
370 - var originalHeight=new WeakMap();
371 - var originalMargin=new WeakMap();
382 + var heightManaged=new Set();
383 + var sectionManaged=new Set();
384 + var contentManaged=new Set();
372 372  
386 + var originalMinHeight=new WeakMap();
387 + var originalMarginTop=new WeakMap();
388 +
373 373   var resizeTimer=0;
374 - var running=false;
390 + var lastWidth=-1;
391 + var hasApplied=false;
392 + var applying=false;
375 375  
376 - var THEMES=[
377 - {
378 - name:"BLUE",
379 - accent:"#21b6d7",
380 - bg:"#eaf7fb",
381 - text:"#315d68",
382 - labels:[
383 - "HIER STARTEN",
384 - "ENTSCHEIDEN",
385 - "ÜBERPRÜFEN & ANPASSEN",
386 - "START HERE",
387 - "DECIDE",
388 - "REVIEW & ADAPT"
389 - ]
390 - },
391 - {
392 - name:"RED",
393 - accent:"#e94b13",
394 - bg:"#fff1ec",
395 - text:"#8a4d36",
396 - labels:[
397 - "PLANEN & ORGANISIEREN",
398 - "ORGANISIEREN & DOKUMENTIEREN",
399 - "PUBLIZIEREN & PRÄSENTIEREN",
400 - "PLAN & ORGANISE",
401 - "PLAN & ORGANIZE",
402 - "ORGANISE & DOCUMENT",
403 - "ORGANIZE & DOCUMENT",
404 - "PUBLISH & PRESENT"
405 - ]
406 - },
407 - {
408 - name:"YELLOW",
409 - accent:"#f0aa00",
410 - bg:"#fff8e7",
411 - text:"#785b17",
412 - labels:[
413 - "SCHÜTZEN & KLÄREN",
414 - "STANDARDISIEREN & IDENTIFIZIEREN",
415 - "ÖFFNEN, NACHNUTZEN & AUFFINDBAR MACHEN",
416 - "PROTECT & CLARIFY",
417 - "STANDARDISE & IDENTIFY",
418 - "STANDARDIZE & IDENTIFY",
419 - "OPEN, REUSE & MAKE FINDABLE"
420 - ]
421 - },
422 - {
423 - name:"GREEN",
424 - accent:"#38b76a",
425 - bg:"#edf9f1",
426 - text:"#3f7451",
427 - labels:[
428 - "INFRASTRUKTUR EINRICHTEN",
429 - "INFRASTRUKTUR & ZUSAMMENARBEIT EINRICHTEN",
430 - "SPEICHERN & ZUGRIFF STEUERN",
431 - "REPOSITORIUM, ERHALTEN & ÜBERGEBEN",
432 - "SET UP INFRASTRUCTURE",
433 - "SET UP INFRASTRUCTURE & COLLABORATION",
434 - "STORE & CONTROL ACCESS",
435 - "REPOSITORY, PRESERVE & HAND OVER"
436 - ]
394 + function num(v){
395 + var n=parseFloat(v);
396 + return Number.isFinite(n)?n:0;
397 + }
398 +
399 + function visible(el,minW,minH){
400 + if(!el||el.nodeType!==1)return false;
401 +
402 + var s=getComputedStyle(el);
403 +
404 + if(
405 + s.display==="none"||
406 + s.visibility==="hidden"||
407 + Number(s.opacity)===0
408 + ){
409 + return false;
437 437   }
438 - ];
439 439  
440 - function num(value){
441 - var n=parseFloat(value);
442 - return Number.isFinite(n)?n:0;
412 + var r=el.getBoundingClientRect();
413 +
414 + return (
415 + r.width>=(minW||1)&&
416 + r.height>=(minH||1)
417 + );
443 443   }
444 444  
445 - function norm(value){
446 - return String(value||"")
420 + function normalizeText(text){
421 + return String(text||"")
447 447   .replace(/\s+/g," ")
448 448   .trim()
449 449   .toUpperCase();
450 450   }
451 451  
452 - function visible(el){
453 - if(!el||el.nodeType!==1)return false;
427 + function excluded(el){
428 + return (
429 + !el.matches||
430 + el.matches(
431 + "button,input,select,textarea,option,label,img,svg,canvas,table,thead,tbody,tfoot,tr,td,th,summary,script,style,noscript,template,.btn,.chip,[role=button]"
432 + )
433 + );
434 + }
454 454  
455 - var style=getComputedStyle(el);
456 - var rect=el.getBoundingClientRect();
436 + /* ---------- Custom coloured callouts ---------- */
457 457  
438 + function nativeCallout(el){
439 + var c=
440 + typeof el.className==="string"
441 + ? el.className.toLowerCase()
442 + : "";
443 +
458 458   return (
459 - style.display!=="none"&&
460 - style.visibility!=="hidden"&&
461 - rect.width>20&&
462 - rect.height>4
445 + /(^|\s)(infomessage|warningmessage|successmessage|errormessage)(\s|$)/.test(c)
463 463   );
464 464   }
465 465  
466 - function saveStyle(el,map,property){
449 + function customCallout(el){
450 + if(!visible(el,220,42)||excluded(el))return false;
451 +
452 + var r=el.getBoundingClientRect();
453 +
454 + if(r.height>520)return false;
455 +
456 + var s=getComputedStyle(el);
457 +
458 + var bg=
459 + s.backgroundColor&&
460 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
461 + s.backgroundColor!=="transparent"&&
462 + s.backgroundColor!=="rgb(255, 255, 255)";
463 +
464 + var left=
465 + num(s.borderLeftWidth);
466 +
467 + return (
468 + bg&&
469 + left>=2
470 + );
471 + }
472 +
473 + function harmoniseCustomCallouts(){
474 + Array.from(
475 + root.querySelectorAll(
476 + "div,aside,section"
477 + )
478 + ).forEach(function(el){
479 + if(nativeCallout(el))return;
480 + if(!customCallout(el))return;
481 +
482 + var s=getComputedStyle(el);
483 +
484 + el.setAttribute(
485 + "data-sn-callout-harmonized",
486 + "1"
487 + );
488 +
489 + if(
490 + s.borderLeftColor
491 + ){
492 + el.style.setProperty(
493 + "--sn-callout-accent",
494 + s.borderLeftColor
495 + );
496 + }
497 + });
498 + }
499 +
500 + /* ---------- Restore only values V7 itself changed ---------- */
501 +
502 + function saveOriginal(el,map,property){
467 467   if(map.has(el))return;
468 468  
469 469   map.set(el,{
470 - value:el.style.getPropertyValue(property),
471 - priority:el.style.getPropertyPriority(property)
506 + value:
507 + el.style.getPropertyValue(
508 + property
509 + ),
510 + priority:
511 + el.style.getPropertyPriority(
512 + property
513 + )
472 472   });
473 473   }
474 474  
475 - function restoreStyle(el,map,property){
517 + function restoreOriginal(el,map,property){
476 476   var original=map.get(el);
477 477  
478 478   if(original&&original.value){
... ... @@ -482,845 +482,770 @@
482 482   original.priority||""
483 483   );
484 484   }else{
485 - el.style.removeProperty(property);
527 + el.style.removeProperty(
528 + property
529 + );
486 486   }
487 487   }
488 488  
489 489   function resetRuntime(){
490 - managedHeight.forEach(function(el){
491 - restoreStyle(
534 + heightManaged.forEach(function(el){
535 + restoreOriginal(
492 492   el,
493 - originalHeight,
537 + originalMinHeight,
494 494   "min-height"
495 495   );
540 +
496 496   el.removeAttribute(
497 - "data-sn-master-equal-height"
542 + "data-sn-equal-height-managed"
498 498   );
499 499   });
500 500  
501 - managedMargin.forEach(function(el){
502 - restoreStyle(
546 + sectionManaged.forEach(function(el){
547 + restoreOriginal(
503 503   el,
504 - originalMargin,
549 + originalMarginTop,
505 505   "margin-top"
506 506   );
552 +
507 507   el.removeAttribute(
508 - "data-sn-master-first-row"
554 + "data-sn-first-section-aligned"
509 509   );
510 510   });
511 511  
512 - managedHeight.clear();
513 - managedMargin.clear();
514 - }
558 + contentManaged.forEach(function(el){
559 + restoreOriginal(
560 + el,
561 + originalMarginTop,
562 + "margin-top"
563 + );
515 515  
516 - function themeFor(text){
517 - var value=norm(text);
565 + el.removeAttribute(
566 + "data-sn-first-content-aligned"
567 + );
568 + });
518 518  
519 - for(var i=0;i<THEMES.length;i++){
520 - for(var j=0;j<THEMES[i].labels.length;j++){
521 - var label=THEMES[i].labels[j];
522 -
523 - if(
524 - value===label||
525 - value.indexOf(label+" ")===0||
526 - value.indexOf(label+" -")===0||
527 - value.indexOf(label+" ·")===0
528 - ){
529 - return THEMES[i];
530 - }
531 - }
532 - }
533 -
534 - return null;
570 + heightManaged.clear();
571 + sectionManaged.clear();
572 + contentManaged.clear();
535 535   }
536 536  
537 - /* =======================================================
538 - PLACEHOLDER BADGE
539 - ======================================================= */
575 + /* ---------- Card detection ---------- */
540 540  
541 - function applyPlaceholderBadge(){
542 - if(!PLACEHOLDER)return;
577 + function namedLikeBox(el){
578 + var c=
579 + typeof el.className==="string"
580 + ? el.className
581 + : "";
543 543  
583 + return /(^|[\s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([\s_-]|$)/i.test(c);
584 + }
585 +
586 + function boxLike(el){
544 544   if(
545 - document.querySelector(
546 - ".sn-coming-soon-badge"
547 - )
588 + !visible(el,MIN_W,MIN_H)||
589 + excluded(el)
548 548   ){
549 - return;
591 + return false;
550 550   }
551 551  
552 - var title=
553 - document.querySelector(
554 - "#document-title"
555 - )||
556 - document.querySelector(
557 - "h1"
558 - );
594 + var s=getComputedStyle(el);
595 + var r=el.getBoundingClientRect();
559 559  
560 - if(!title)return;
597 + var border=
598 + num(s.borderTopWidth)+
599 + num(s.borderRightWidth)+
600 + num(s.borderBottomWidth)+
601 + num(s.borderLeftWidth);
561 561  
562 - var badge=
563 - document.createElement(
564 - "span"
565 - );
566 -
567 - badge.className=
568 - "sn-coming-soon-badge";
569 -
570 - badge.textContent=
571 - PLACEHOLDER_LABEL;
572 -
573 - badge.setAttribute(
574 - "aria-label",
575 - PLACEHOLDER_LABEL
603 + var radius=Math.max(
604 + num(s.borderTopLeftRadius),
605 + num(s.borderTopRightRadius),
606 + num(s.borderBottomLeftRadius),
607 + num(s.borderBottomRightRadius)
576 576   );
577 577  
578 - title.appendChild(
579 - badge
580 - );
581 - }
610 + var bg=
611 + s.backgroundColor&&
612 + s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
613 + s.backgroundColor!=="transparent";
582 582  
583 - /* =======================================================
584 - DETAIL PAGE OWN-ACCENT PROPAGATION
585 - ======================================================= */
615 + var shadow=
616 + s.boxShadow&&
617 + s.boxShadow!=="none";
586 586  
587 - function parseRGB(value){
588 - var match=String(value||"").match(
589 - /rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i
619 + return (
620 + namedLikeBox(el)||
621 + border>=1||
622 + (bg&&radius>=3)||
623 + (shadow&&r.width>=180)
590 590   );
591 -
592 - if(!match)return null;
593 -
594 - return [
595 - Number(match[1]),
596 - Number(match[2]),
597 - Number(match[3])
598 - ];
599 599   }
600 600  
601 - function distance(a,b){
602 - return Math.sqrt(
603 - Math.pow(a[0]-b[0],2)+
604 - Math.pow(a[1]-b[1],2)+
605 - Math.pow(a[2]-b[2],2)
606 - );
607 - }
627 + function pickTarget(child){
628 + if(!visible(child,MIN_W,MIN_H))return null;
608 608  
609 - function paletteMatch(cssColor){
610 - var rgb=parseRGB(cssColor);
630 + if(boxLike(child))return child;
611 611  
612 - if(!rgb)return null;
632 + var cr=child.getBoundingClientRect();
633 + var q=[];
613 613  
614 - var palette=[
615 - {hex:"#21b6d7",rgb:[33,182,215]},
616 - {hex:"#e94b13",rgb:[233,75,19]},
617 - {hex:"#f0aa00",rgb:[240,170,0]},
618 - {hex:"#38b76a",rgb:[56,183,106]}
619 - ];
635 + Array.from(child.children||[]).forEach(function(el){
636 + q.push({el:el,depth:1});
637 + });
620 620  
621 621   var best=null;
622 - var bestDistance=Infinity;
640 + var bestScore=-Infinity;
623 623  
624 - palette.forEach(function(item){
625 - var d=distance(rgb,item.rgb);
642 + while(q.length){
643 + var item=q.shift();
644 + var el=item.el;
626 626  
627 - if(d<bestDistance){
628 - best=item;
629 - bestDistance=d;
630 - }
631 - });
646 + if(!visible(el,MIN_W,MIN_H))continue;
632 632  
633 - return bestDistance<=30
634 - ?best
635 - :null;
636 - }
648 + var r=el.getBoundingClientRect();
637 637  
638 - function propagateDetailAccents(){
639 - if(PAGE_KIND!=="detail")return;
640 -
641 - Array.from(
642 - root.querySelectorAll(
643 - ".arsn-call,.dmp-call,.ido-call,.pp-call,"+
644 - ".arsn-source,.dmp-source,.ido-source,.pp-source"
645 - )
646 - ).forEach(function(el){
647 - if(!visible(el))return;
648 -
649 - var style=getComputedStyle(el);
650 -
651 651   if(
652 - num(style.borderTopWidth)>=2
651 + r.width>=cr.width*.68&&
652 + boxLike(el)
653 653   ){
654 - var match=
655 - paletteMatch(
656 - style.borderTopColor
654 + var widthFit=
655 + 1-Math.min(
656 + 1,
657 + Math.abs(cr.width-r.width)/
658 + Math.max(1,cr.width)
657 657   );
658 658  
659 - if(match){
660 - el.style.setProperty(
661 - "border-left",
662 - "4px solid "+match.hex,
663 - "important"
661 + var topFit=
662 + 1-Math.min(
663 + 1,
664 + Math.abs(cr.top-r.top)/100
664 664   );
666 +
667 + var score=
668 + widthFit*6+
669 + topFit*3+
670 + Math.min(r.height,900)/450+
671 + Math.min(r.width*r.height,900000)/900000;
672 +
673 + if(score>bestScore){
674 + best=el;
675 + bestScore=score;
665 665   }
666 666   }
667 - });
668 668  
669 - // Warning families: explicit safety override.
670 - Array.from(
671 - root.querySelectorAll(
672 - ".arsn-warn,.dmp-warn,.ido-warn,.pp-warn,"+
673 - ".warn,.warning,.warningmessage,.errormessage"
674 - )
675 - ).forEach(function(el){
676 - if(!visible(el))return;
679 + if(item.depth<SEARCH_DEPTH){
680 + Array.from(el.children||[]).forEach(function(next){
681 + q.push({
682 + el:next,
683 + depth:item.depth+1
684 + });
685 + });
686 + }
687 + }
677 677  
678 - el.style.setProperty(
679 - "border-left",
680 - "4px solid #e94b13",
681 - "important"
682 - );
683 - });
689 + return best;
690 + }
684 684  
685 - // Info families: explicit orientation override.
686 - Array.from(
687 - root.querySelectorAll(
688 - ".arsn-info,.dmp-info,.ido-info,.pp-info,.infomessage"
689 - )
690 - ).forEach(function(el){
691 - if(!visible(el))return;
692 + function groupRows(entries){
693 + var groups=[];
692 692  
693 - el.style.setProperty(
694 - "border-left",
695 - "4px solid #21b6d7",
696 - "important"
697 - );
698 - });
699 - }
695 + entries
696 + .slice()
697 + .sort(function(a,b){
698 + return (
699 + a.rect.top-b.rect.top||
700 + a.rect.left-b.rect.left
701 + );
702 + })
703 + .forEach(function(entry){
704 + var group=null;
700 700  
701 - /* =======================================================
702 - OVERVIEW SEMANTIC THEME PALETTE
703 - ======================================================= */
706 + for(var i=0;i<groups.length;i++){
707 + if(
708 + Math.abs(
709 + groups[i].top-
710 + entry.rect.top
711 + )<=ROW_TOLERANCE
712 + ){
713 + group=groups[i];
714 + break;
715 + }
716 + }
704 704  
705 - function smallestThemeHeaders(){
706 - return Array.from(
707 - root.querySelectorAll(
708 - "div,span,p,strong,h3,h4,h5,h6"
709 - )
710 - ).filter(function(el){
711 - if(!visible(el))return false;
712 - if(!themeFor(el.textContent))return false;
718 + if(!group){
719 + group={
720 + top:entry.rect.top,
721 + entries:[]
722 + };
723 + groups.push(group);
724 + }
713 713  
714 - return !Array.from(
715 - el.children||[]
716 - ).some(function(child){
717 - return !!themeFor(
718 - child.textContent
719 - );
726 + group.entries.push(entry);
720 720   });
721 - });
728 +
729 + return groups;
722 722   }
723 723  
724 - function findOverviewCard(header){
725 - var node=header.parentElement;
726 - var candidates=[];
732 + function validRow(group){
733 + if(group.entries.length<2)return false;
727 727  
728 - while(node&&node!==root){
729 - var rect=node.getBoundingClientRect();
730 - var style=getComputedStyle(node);
735 + var lefts=
736 + group.entries
737 + .map(function(e){return e.rect.left;})
738 + .sort(function(a,b){return a-b;});
731 731  
732 - var border=
733 - num(style.borderTopWidth)+
734 - num(style.borderRightWidth)+
735 - num(style.borderBottomWidth)+
736 - num(style.borderLeftWidth);
740 + if(
741 + lefts[lefts.length-1]-
742 + lefts[0]<
743 + 80
744 + ){
745 + return false;
746 + }
737 737  
738 - if(
739 - rect.width>=220&&
740 - rect.width<=800&&
741 - rect.height>=120&&
742 - border>=1
743 - ){
744 - candidates.push(node);
745 - }
748 + var widths=
749 + group.entries
750 + .map(function(e){return e.rect.width;})
751 + .filter(function(w){return w>0;});
746 746  
747 - node=node.parentElement;
748 - }
753 + if(widths.length<2)return false;
749 749  
750 - if(!candidates.length)return null;
755 + var minW=
756 + Math.min.apply(Math,widths);
751 751  
752 - candidates.sort(function(a,b){
753 - return (
754 - b.getBoundingClientRect().height-
755 - a.getBoundingClientRect().height
758 + var maxW=
759 + Math.max.apply(Math,widths);
760 +
761 + return (
762 + maxW/
763 + Math.max(1,minW)<=
764 + 1.9
765 + );
766 + }
767 +
768 + /* ---------- Known ARSN first section bars ---------- */
769 +
770 + function knownBar(el){
771 + var text=
772 + normalizeText(
773 + el.textContent
756 756   );
757 - });
758 758  
759 - return candidates[0];
776 + return (
777 + text==="HIER STARTEN"||
778 + text==="ORGANISIEREN & DOKUMENTIEREN"||
779 + text==="ENTSCHEIDEN"||
780 + text==="START HERE"||
781 + text==="GET STARTED"||
782 + text==="ORGANISE & DOCUMENT"||
783 + text==="ORGANIZE & DOCUMENT"||
784 + text==="DECIDE"
785 + );
760 760   }
761 761  
762 - function rowCandidates(link,card){
763 - var result=[];
788 + function findKnownBar(card){
764 764   var cardRect=
765 765   card.getBoundingClientRect();
766 766  
767 - var node=link;
792 + var rows=
793 + Array.from(
794 + card.querySelectorAll(
795 + "div,p,span,strong,h3,h4,h5,h6"
796 + )
797 + )
798 + .filter(function(el){
799 + if(!visible(el,20,6))return false;
800 + if(!knownBar(el))return false;
768 768  
769 - while(node&&node!==card){
770 - if(visible(node)){
771 - var rect=
772 - node.getBoundingClientRect();
802 + var r=el.getBoundingClientRect();
773 773  
774 - if(
775 - rect.height<=105&&
776 - rect.width>=
777 - cardRect.width*.50&&
778 - rect.width<=
779 - cardRect.width*1.03
780 - ){
781 - result.push(node);
782 - }
783 - }
804 + var rel=
805 + r.top-
806 + cardRect.top;
784 784  
785 - node=node.parentElement;
786 - }
787 -
788 - return result;
789 - }
790 -
791 - function chooseOuterRow(
792 - candidates,
793 - link
794 - ){
795 - if(!candidates.length){
796 - return link;
797 - }
798 -
799 - var bordered=
800 - candidates.filter(function(el){
801 801   return (
802 - num(
803 - getComputedStyle(el)
804 - .borderLeftWidth
805 - )>=1
809 + rel>=35&&
810 + rel<=
811 + cardRect.height*.75
806 806   );
813 + })
814 + .sort(function(a,b){
815 + return (
816 + a.getBoundingClientRect().top-
817 + b.getBoundingClientRect().top
818 + );
807 807   });
808 808  
809 - var pool=
810 - bordered.length
811 - ?bordered
812 - :candidates;
821 + return rows[0]||null;
822 + }
813 813  
814 - pool.sort(function(a,b){
815 - var ar=
816 - a.getBoundingClientRect();
824 + function alignKnownBars(cards){
825 + var rows=
826 + cards.map(function(card){
827 + var bar=
828 + findKnownBar(card);
817 817  
818 - var br=
819 - b.getBoundingClientRect();
830 + if(!bar)return null;
820 820  
821 - return (
822 - ar.left-br.left||
823 - br.width-ar.width
824 - );
825 - });
832 + var cr=
833 + card.getBoundingClientRect();
826 826  
827 - return pool[0]||link;
828 - }
835 + var br=
836 + bar.getBoundingClientRect();
829 829  
830 - function applyOverviewPalette(){
831 - if(PAGE_KIND!=="overview"){
832 - return [];
838 + return {
839 + bar:bar,
840 + relativeTop:
841 + br.top-
842 + cr.top
843 + };
844 + });
845 +
846 + if(
847 + rows.some(function(row){
848 + return !row;
849 + })
850 + ){
851 + return false;
833 833   }
834 834  
835 - var headers=
836 - smallestThemeHeaders()
837 - .map(function(el){
838 - return {
839 - el:el,
840 - theme:
841 - themeFor(
842 - el.textContent
843 - ),
844 - card:
845 - findOverviewCard(
846 - el
847 - )
848 - };
854 + var target=
855 + Math.max.apply(
856 + Math,
857 + rows.map(function(row){
858 + return row.relativeTop;
849 849   })
850 - .filter(function(row){
851 - return !!row.card;
852 - });
860 + );
853 853  
854 - var unique=[];
855 - var seenHeaders=new Set();
862 + rows.forEach(function(row){
863 + var delta=
864 + target-
865 + row.relativeTop;
856 856  
857 - headers.forEach(function(row){
858 - var key=
859 - row.theme.name+
860 - "|"+
861 - Math.round(
862 - row.card
863 - .getBoundingClientRect()
864 - .left
865 - )+
866 - "|"+
867 - Math.round(
868 - row.el
869 - .getBoundingClientRect()
870 - .top
871 - );
867 + if(delta<=1)return;
872 872  
873 - if(!seenHeaders.has(key)){
874 - seenHeaders.add(key);
875 - unique.push(row);
876 - }
877 - });
869 + saveOriginal(
870 + row.bar,
871 + originalMarginTop,
872 + "margin-top"
873 + );
878 878  
879 - unique.forEach(function(row){
880 - var headerRect=
881 - row.el
882 - .getBoundingClientRect();
875 + var current=
876 + num(
877 + getComputedStyle(
878 + row.bar
879 + ).marginTop
880 + );
883 883  
884 - row.el.style.setProperty(
885 - "background",
886 - row.theme.bg,
887 - "important"
882 + row.bar.style.setProperty(
883 + "margin-top",
884 + (
885 + current+
886 + delta
887 + )+"px"
888 888   );
889 889  
890 - row.el.style.setProperty(
891 - "color",
892 - row.theme.text,
893 - "important"
890 + row.bar.setAttribute(
891 + "data-sn-first-section-aligned",
892 + "7"
894 894   );
895 895  
896 - row.el.style.setProperty(
897 - "text-align",
898 - "left",
899 - "important"
895 + sectionManaged.add(
896 + row.bar
900 900   );
898 + });
901 901  
902 - row.el.style.setProperty(
903 - "word-spacing",
904 - "normal",
905 - "important"
906 - );
900 + return true;
901 + }
907 907  
908 - var later=
909 - unique
910 - .filter(function(other){
911 - return (
912 - other.card===row.card&&
913 - other.el
914 - .getBoundingClientRect()
915 - .top>
916 - headerRect.top+2
917 - );
918 - })
919 - .sort(function(a,b){
920 - return (
921 - a.el
922 - .getBoundingClientRect()
923 - .top-
924 - b.el
925 - .getBoundingClientRect()
926 - .top
927 - );
928 - });
903 + /* ---------- Generic first content after heading ---------- */
929 929  
930 - var stopTop=
931 - later.length
932 - ?later[0].el
933 - .getBoundingClientRect()
934 - .top
935 - :Infinity;
905 + function heading(card){
906 + var cr=
907 + card.getBoundingClientRect();
936 936  
909 + var rows=
937 937   Array.from(
938 - row.card.querySelectorAll("a")
939 - ).forEach(function(link){
940 - if(!visible(link))return;
911 + card.querySelectorAll(
912 + "h1,h2,h3,h4,h5,h6,.box-title,[role=heading]"
913 + )
914 + )
915 + .filter(function(el){
916 + if(!visible(el,20,8))return false;
941 941  
942 - var rect=
943 - link.getBoundingClientRect();
918 + var r=el.getBoundingClientRect();
944 944  
945 - if(
946 - rect.top<=
947 - headerRect.bottom-2||
948 - rect.top>=
949 - stopTop-2
950 - ){
951 - return;
952 - }
920 + var rel=
921 + r.top-
922 + cr.top;
953 953  
954 - var candidates=
955 - rowCandidates(
956 - link,
957 - row.card
958 - );
959 -
960 - var target=
961 - chooseOuterRow(
962 - candidates,
963 - link
964 - );
965 -
966 - candidates.forEach(function(el){
967 - el.style.setProperty(
968 - "border-left",
969 - "0 solid transparent",
970 - "important"
971 - );
972 - });
973 -
974 - target.style.setProperty(
975 - "border-left",
976 - "3px solid "+
977 - row.theme.accent,
978 - "important"
924 + return (
925 + rel>=0&&
926 + rel<=
927 + Math.min(
928 + 160,
929 + cr.height*.45
930 + )
979 979   );
980 -
981 - target.style.setProperty(
982 - "box-sizing",
983 - "border-box",
984 - "important"
932 + })
933 + .sort(function(a,b){
934 + return (
935 + a.getBoundingClientRect().top-
936 + b.getBoundingClientRect().top
985 985   );
986 986   });
987 - });
988 988  
989 - return unique;
940 + return rows[0]||null;
990 990   }
991 991  
992 - /* =======================================================
993 - OVERVIEW CARD HEIGHT + FIRST THEMATIC ROW
994 - ======================================================= */
943 + function firstContent(card,h){
944 + if(!h)return null;
995 995  
996 - function alignOverviewCards(headers){
997 - if(
998 - PAGE_KIND!=="overview"||
999 - window.innerWidth<=MOBILE_MAX
1000 - ){
1001 - return;
1002 - }
946 + var cr=
947 + card.getBoundingClientRect();
1003 1003  
1004 - var cards=[];
1005 - var seen=new Set();
949 + var hr=
950 + h.getBoundingClientRect();
1006 1006  
1007 - headers.forEach(function(row){
1008 - if(!seen.has(row.card)){
1009 - seen.add(row.card);
1010 - cards.push(row.card);
1011 - }
1012 - });
952 + var rows=
953 + Array.from(
954 + card.querySelectorAll(
955 + "p,div,ul,ol,dl,blockquote"
956 + )
957 + )
958 + .filter(function(el){
959 + if(
960 + el===h||
961 + h.contains(el)||
962 + el.contains(h)
963 + ){
964 + return false;
965 + }
1013 1013  
1014 - if(cards.length<2){
1015 - return;
1016 - }
967 + if(!visible(el,30,8))return false;
1017 1017  
1018 - // Group cards by actual top position.
1019 - var groups=[];
969 + var text=
970 + String(el.textContent||"")
971 + .replace(/\s+/g," ")
972 + .trim();
1020 1020  
1021 - cards.forEach(function(card){
1022 - var top=
1023 - card.getBoundingClientRect().top;
974 + if(text.length<3)return false;
1024 1024  
1025 - var group=null;
976 + var r=el.getBoundingClientRect();
1026 1026  
1027 - for(var i=0;i<groups.length;i++){
1028 1028   if(
1029 - Math.abs(
1030 - groups[i].top-top
1031 - )<=ROW_TOLERANCE
979 + r.top<
980 + hr.bottom-1
1032 1032   ){
1033 - group=groups[i];
1034 - break;
982 + return false;
1035 1035   }
1036 - }
1037 1037  
1038 - if(!group){
1039 - group={
1040 - top:top,
1041 - cards:[]
1042 - };
1043 - groups.push(group);
1044 - }
985 + if(
986 + r.top-cr.top>
987 + Math.min(
988 + 280,
989 + cr.height*.65
990 + )
991 + ){
992 + return false;
993 + }
1045 1045  
1046 - group.cards.push(card);
1047 - });
995 + if(
996 + r.height>
997 + cr.height*.58
998 + ){
999 + return false;
1000 + }
1048 1048  
1049 - groups.forEach(function(group){
1050 - if(group.cards.length<2){
1051 - return;
1052 - }
1002 + return true;
1003 + })
1004 + .sort(function(a,b){
1005 + var ar=
1006 + a.getBoundingClientRect();
1053 1053  
1054 - var firstHeaders=
1055 - group.cards.map(function(card){
1056 - var candidates=
1057 - headers
1058 - .filter(function(row){
1059 - return row.card===card;
1060 - })
1061 - .sort(function(a,b){
1062 - return (
1063 - a.el
1064 - .getBoundingClientRect()
1065 - .top-
1066 - b.el
1067 - .getBoundingClientRect()
1068 - .top
1069 - );
1070 - });
1008 + var br=
1009 + b.getBoundingClientRect();
1071 1071  
1072 - return candidates.length
1073 - ?candidates[0]
1074 - :null;
1075 - });
1011 + return (
1012 + ar.top-br.top||
1013 + ar.height-br.height
1014 + );
1015 + });
1076 1076  
1077 - if(
1078 - !firstHeaders.some(function(row){
1079 - return !row;
1080 - })
1081 - ){
1082 - var targetTop=
1083 - Math.max.apply(
1084 - Math,
1085 - firstHeaders.map(function(row){
1086 - return (
1087 - row.el
1088 - .getBoundingClientRect()
1089 - .top-
1090 - row.card
1091 - .getBoundingClientRect()
1092 - .top
1093 - );
1094 - })
1095 - );
1017 + return rows[0]||null;
1018 + }
1096 1096  
1097 - firstHeaders.forEach(function(row){
1098 - var relativeTop=
1099 - row.el
1100 - .getBoundingClientRect()
1101 - .top-
1102 - row.card
1103 - .getBoundingClientRect()
1104 - .top;
1020 + function alignGeneric(cards){
1021 + var rows=
1022 + cards.map(function(card){
1023 + var h=
1024 + heading(card);
1105 1105  
1106 - var delta=
1107 - targetTop-relativeTop;
1026 + var c=
1027 + firstContent(
1028 + card,
1029 + h
1030 + );
1108 1108  
1109 - if(delta>1){
1110 - saveStyle(
1111 - row.el,
1112 - originalMargin,
1113 - "margin-top"
1114 - );
1032 + if(!h||!c)return null;
1115 1115  
1116 - var current=
1117 - num(
1118 - getComputedStyle(
1119 - row.el
1120 - ).marginTop
1121 - );
1034 + var cr=
1035 + card.getBoundingClientRect();
1122 1122  
1123 - row.el.style.setProperty(
1124 - "margin-top",
1125 - (
1126 - current+delta
1127 - )+"px"
1128 - );
1037 + var rr=
1038 + c.getBoundingClientRect();
1129 1129  
1130 - row.el.setAttribute(
1131 - "data-sn-master-first-row",
1132 - "1"
1133 - );
1040 + return {
1041 + content:c,
1042 + relativeTop:
1043 + rr.top-cr.top
1044 + };
1045 + });
1134 1134  
1135 - managedMargin.add(
1136 - row.el
1137 - );
1138 - }
1139 - });
1140 - }
1047 + if(
1048 + rows.some(function(row){
1049 + return !row;
1050 + })
1051 + ){
1052 + return false;
1053 + }
1141 1141  
1142 - var maxHeight=0;
1055 + var target=
1056 + Math.max.apply(
1057 + Math,
1058 + rows.map(function(row){
1059 + return row.relativeTop;
1060 + })
1061 + );
1143 1143  
1144 - group.cards.forEach(function(card){
1145 - maxHeight=
1146 - Math.max(
1147 - maxHeight,
1148 - card
1149 - .getBoundingClientRect()
1150 - .height
1151 - );
1152 - });
1063 + rows.forEach(function(row){
1064 + var delta=
1065 + target-
1066 + row.relativeTop;
1153 1153  
1154 - maxHeight=
1155 - Math.ceil(maxHeight);
1068 + if(delta<=1)return;
1156 1156  
1157 - group.cards.forEach(function(card){
1158 - saveStyle(
1159 - card,
1160 - originalHeight,
1161 - "min-height"
1162 - );
1070 + saveOriginal(
1071 + row.content,
1072 + originalMarginTop,
1073 + "margin-top"
1074 + );
1163 1163  
1164 - card.style.setProperty(
1165 - "min-height",
1166 - maxHeight+"px"
1076 + var current=
1077 + num(
1078 + getComputedStyle(
1079 + row.content
1080 + ).marginTop
1167 1167   );
1168 1168  
1169 - card.setAttribute(
1170 - "data-sn-master-equal-height",
1171 - "1"
1172 - );
1083 + row.content.style.setProperty(
1084 + "margin-top",
1085 + (
1086 + current+
1087 + delta
1088 + )+"px"
1089 + );
1173 1173  
1174 - managedHeight.add(
1175 - card
1176 - );
1177 - });
1091 + row.content.setAttribute(
1092 + "data-sn-first-content-aligned",
1093 + "7"
1094 + );
1095 +
1096 + contentManaged.add(
1097 + row.content
1098 + );
1178 1178   });
1100 +
1101 + return true;
1179 1179   }
1180 1180  
1181 - /* =======================================================
1182 - DETAIL GRID EQUAL HEIGHT, DESKTOP ONLY
1183 - ======================================================= */
1104 + function applyRow(group){
1105 + if(!validRow(group))return false;
1184 1184  
1185 - function equaliseDetailGrids(){
1107 + var cards=[];
1108 + var seen=new Set();
1109 +
1110 + group.entries.forEach(function(entry){
1111 + if(!seen.has(entry.target)){
1112 + seen.add(entry.target);
1113 + cards.push(entry.target);
1114 + }
1115 + });
1116 +
1117 + if(cards.length<2)return false;
1118 +
1186 1186   if(
1187 - PAGE_KIND!=="detail"||
1188 - window.innerWidth<=MOBILE_MAX
1120 + !alignKnownBars(cards)
1189 1189   ){
1190 - return;
1122 + alignGeneric(cards);
1191 1191   }
1192 1192  
1193 - var selectors=[
1194 - ".arsn-grid",
1195 - ".arsn-grid-2",
1196 - ".arsn-grid-3",
1197 - ".dmp-grid",
1198 - ".ido-grid",
1199 - ".pp-grid"
1200 - ];
1125 + var maxHeight=0;
1201 1201  
1202 - selectors.forEach(function(selector){
1203 - Array.from(
1204 - root.querySelectorAll(selector)
1205 - ).forEach(function(grid){
1206 - var children=
1207 - Array.from(
1208 - grid.children||[]
1209 - ).filter(visible);
1127 + cards.forEach(function(card){
1128 + maxHeight=
1129 + Math.max(
1130 + maxHeight,
1131 + card.getBoundingClientRect().height
1132 + );
1133 + });
1210 1210  
1211 - if(children.length<2){
1212 - return;
1213 - }
1135 + maxHeight=
1136 + Math.ceil(
1137 + maxHeight
1138 + );
1214 1214  
1215 - var groups=[];
1140 + if(maxHeight<MIN_H)return false;
1216 1216  
1217 - children.forEach(function(child){
1218 - var top=
1219 - child
1220 - .getBoundingClientRect()
1221 - .top;
1142 + cards.forEach(function(card){
1143 + saveOriginal(
1144 + card,
1145 + originalMinHeight,
1146 + "min-height"
1147 + );
1222 1222  
1223 - var group=null;
1149 + card.style.setProperty(
1150 + "min-height",
1151 + maxHeight+"px"
1152 + );
1224 1224  
1225 - for(var i=0;i<groups.length;i++){
1226 - if(
1227 - Math.abs(
1228 - groups[i].top-top
1229 - )<=ROW_TOLERANCE
1230 - ){
1231 - group=groups[i];
1232 - break;
1233 - }
1234 - }
1154 + card.setAttribute(
1155 + "data-sn-equal-height-managed",
1156 + "7"
1157 + );
1235 1235  
1236 - if(!group){
1237 - group={
1238 - top:top,
1239 - items:[]
1240 - };
1241 - groups.push(group);
1242 - }
1159 + heightManaged.add(card);
1160 + });
1243 1243  
1244 - group.items.push(child);
1245 - });
1162 + return true;
1163 + }
1246 1246  
1247 - groups.forEach(function(group){
1248 - if(group.items.length<2){
1249 - return;
1250 - }
1165 + function calculate(force){
1166 + if(applying)return;
1251 1251  
1252 - var maxHeight=0;
1168 + var width=
1169 + Math.round(
1170 + window.innerWidth
1171 + );
1253 1253  
1254 - group.items.forEach(function(item){
1255 - maxHeight=
1256 - Math.max(
1257 - maxHeight,
1258 - item
1259 - .getBoundingClientRect()
1260 - .height
1261 - );
1262 - });
1173 + var meaningful=
1174 + lastWidth<0||
1175 + Math.abs(
1176 + width-
1177 + lastWidth
1178 + )>=
1179 + MIN_WIDTH_CHANGE;
1263 1263  
1264 - maxHeight=
1265 - Math.ceil(maxHeight);
1181 + if(
1182 + hasApplied&&
1183 + !force&&
1184 + !meaningful
1185 + ){
1186 + return;
1187 + }
1266 1266  
1267 - group.items.forEach(function(item){
1268 - saveStyle(
1269 - item,
1270 - originalHeight,
1271 - "min-height"
1272 - );
1189 + applying=true;
1273 1273  
1274 - item.style.setProperty(
1275 - "min-height",
1276 - maxHeight+"px"
1277 - );
1191 + try{
1192 + harmoniseCustomCallouts();
1278 1278  
1279 - item.setAttribute(
1280 - "data-sn-master-equal-height",
1281 - "1"
1282 - );
1194 + if(width<=MOBILE_MAX){
1195 + if(hasApplied){
1196 + resetRuntime();
1197 + }
1283 1283  
1284 - managedHeight.add(item);
1285 - });
1199 + hasApplied=false;
1200 + lastWidth=width;
1201 + return;
1202 + }
1203 +
1204 + if(hasApplied){
1205 + resetRuntime();
1206 + }
1207 +
1208 + var parents=
1209 + Array.from(
1210 + root.querySelectorAll("*")
1211 + )
1212 + .filter(function(parent){
1213 + return (
1214 + visible(
1215 + parent,
1216 + MIN_W,
1217 + MIN_H
1218 + )&&
1219 + parent.children&&
1220 + parent.children.length>=2&&
1221 + parent.children.length<=
1222 + MAX_PARENT_CHILDREN
1223 + );
1286 1286   });
1287 - });
1288 - });
1289 - }
1290 1290  
1291 - /* =======================================================
1292 - RUN / RESPONSIVE SCHEDULING
1293 - ======================================================= */
1226 + var touched=
1227 + new Set();
1294 1294  
1295 - function run(){
1296 - if(running)return;
1229 + parents.forEach(function(parent){
1230 + var entries=[];
1297 1297  
1298 - running=true;
1232 + Array.from(
1233 + parent.children
1234 + ).forEach(function(child){
1235 + if(
1236 + !visible(
1237 + child,
1238 + MIN_W,
1239 + MIN_H
1240 + )
1241 + ){
1242 + return;
1243 + }
1299 1299  
1300 - try{
1301 - resetRuntime();
1302 - applyPlaceholderBadge();
1303 - propagateDetailAccents();
1245 + var target=
1246 + pickTarget(child);
1304 1304  
1305 - var headers=
1306 - applyOverviewPalette();
1248 + if(!target)return;
1307 1307  
1308 - if(
1309 - window.innerWidth>
1310 - MOBILE_MAX
1311 - ){
1312 - alignOverviewCards(
1313 - headers
1314 - );
1250 + entries.push({
1251 + target:target,
1252 + rect:
1253 + target.getBoundingClientRect()
1254 + });
1255 + });
1315 1315  
1316 - equaliseDetailGrids();
1317 - }
1257 + groupRows(
1258 + entries
1259 + ).forEach(function(group){
1260 + if(!validRow(group))return;
1261 +
1262 + var targets=
1263 + group.entries.map(function(entry){
1264 + return entry.target;
1265 + });
1266 +
1267 + if(
1268 + !targets.some(function(el){
1269 + return !touched.has(el);
1270 + })
1271 + ){
1272 + return;
1273 + }
1274 +
1275 + if(applyRow(group)){
1276 + targets.forEach(function(el){
1277 + touched.add(el);
1278 + });
1279 + }
1280 + });
1281 + });
1282 +
1283 + lastWidth=width;
1284 + hasApplied=true;
1318 1318   }finally{
1319 - running=false;
1286 + applying=false;
1320 1320   }
1321 1321   }
1322 1322  
1323 - function onResize(){
1290 + function resize(){
1324 1324   if(resizeTimer){
1325 1325   clearTimeout(
1326 1326   resizeTimer
... ... @@ -1331,40 +1331,19 @@
1331 1331   setTimeout(
1332 1332   function(){
1333 1333   resizeTimer=0;
1334 - run();
1301 + calculate(false);
1335 1335   },
1336 1336   RESIZE_DEBOUNCE
1337 1337   );
1338 1338   }
1339 1339  
1340 - async function waitForReady(){
1341 - if(
1342 - document.readyState!=="complete"
1343 - ){
1344 - await new Promise(function(resolve){
1345 - window.addEventListener(
1346 - "load",
1347 - resolve,
1348 - {once:true}
1349 - );
1350 - });
1351 - }
1352 -
1353 - if(
1354 - document.fonts&&
1355 - document.fonts.ready
1356 - ){
1357 - try{
1358 - await document.fonts.ready;
1359 - }catch(error){}
1360 - }
1361 -
1307 + function waitImages(){
1362 1362   var images=
1363 1363   Array.from(
1364 1364   root.querySelectorAll("img")
1365 1365   );
1366 1366  
1367 - await Promise.all(
1313 + return Promise.all(
1368 1368   images.map(function(img){
1369 1369   if(img.complete){
1370 1370   return Promise.resolve();
... ... @@ -1389,20 +1389,46 @@
1389 1389   });
1390 1390   })
1391 1391   );
1338 + }
1392 1392  
1393 - run();
1340 + async function initial(){
1341 + if(
1342 + document.readyState!==
1343 + "complete"
1344 + ){
1345 + await new Promise(function(resolve){
1346 + addEventListener(
1347 + "load",
1348 + resolve,
1349 + {once:true}
1350 + );
1351 + });
1352 + }
1353 +
1354 + if(
1355 + document.fonts&&
1356 + document.fonts.ready
1357 + ){
1358 + try{
1359 + await document.fonts.ready;
1360 + }catch(error){}
1361 + }
1362 +
1363 + await waitImages();
1364 +
1365 + // Exactly one initial layout calculation.
1366 + calculate(true);
1394 1394   }
1395 1395  
1396 - window.addEventListener(
1369 + addEventListener(
1397 1397   "resize",
1398 - onResize,
1371 + resize,
1399 1399   {passive:true}
1400 1400   );
1401 1401  
1402 - waitForReady();
1375 + initial();
1403 1403  })();
1404 1404  </script>
1405 1405  
1406 -<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_END -->
1407 -{{/html}}
1408 -
1379 +<!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->
1380 +{{/html}}\n